既然认为它是好的,就要发挥到极限-系列之二单元测试

既然测试是好的,那就把它发挥到极限。
测试是好的,这一点无可厚非,几乎做软件的人都是认可的,本篇只是谈谈测试中的单元测试部分,单元测试的目的是为了保证类中的方法是符合设计时的需求的,需求驱动似的类实现,^_^

单元测试的好处
1、保证类对于设计以及需求的符合
      在没有单元测试的情况下,其实是很难保证类对于设计以及需求的符合的,很多情况往往会因为开发人员本身的因素将实现代码复杂化,并且编写出很多设计和需求根本不需要的东西。
2、降低调试的复杂性
      想想在没有单元测试的情况下,调试通常是集成时才做的,这个时候要通过慢慢的跟踪来查找问题的原因,而在web系统中就更痛苦了,总是要重启,如果不想那么痛苦,就采用单元测试吧。
3、减少集成时出错的机率
      单元测试可保证暴露给外部的API的正确性,减少要通过集成才发现错误的现象。
4、保证重构和简单设计的可行
      想想,如果没有单元测试,怎么敢对代码做重构呢,如果没有单元测试,简单设计很难通过重构去演变成为将来更好的设计。

单元测试的实现
单元测试的实现采取的方法通常是xUnit,在Java界就是junit了,最重要的仍然不是工具,而是怎么去实现单元测试的方法,测试驱动开发无疑是最佳的编写测试的方法,首先根据设计或需求编写测试,根据测试编写代码,直到测试通过为止。
在代码出现bug时,一定要先把出现bug的情况补充到测试中去,接下来仍然是修改实现代码,直到测试通过。
单元测试编写的原则其实很简单,就是测试一定情况下类的执行是否符合预期。
还是举例来说:
假设需要编写一个根据用户名和密码验证用户的服务,按照TDD我们首先编写单元测试类,我们应该怎么来编写这个单元测试类呢,一般可按照一个这样的步骤:
1、分析类的输入。
      这点通常是分析类依赖外部什么类,需要在测试类中提前注入。
      以上面的服务来说,通常需要依赖的是用户的Dao类。
2、分析方法的输入造成的输出的影响。
      这点通常是分析方法输入的参数对执行结果造成的影响。
      以上面的服务来说,输入的参数为用户名和密码,这个时候会有几种情况会出现:
      2.1 用户名或密码为null
            在这种情况下,假设期待的输出为false
      2.2 用户名和密码都为null
            在这种情况下,假设期待的输出为false
      2.3 输入的用户名和密码在系统中存在
            在这种情况下,假设期待的输出为true
      2.4 用户名或密码其中一项输入不正确,验证不通过
            在这种情况下,假设期待的抛出AuthronizedException
在经过这样的分析后,就可以开始编写测试类了,编写的测试类如下(示例代码而已):
public class UserServiceTest extends TestCase {

    
private UserDao dao=null;
    
    
private UserService service=null;
    
    
private User user=null;
    
    
public static void main(String[] args) {
        junit.textui.TestRunner.run(UserServiceTest.
class);
    }


    
protected void setUp() throws Exception {
        
super.setUp();
        dao
=new UserDaoImpl();
        user
=new User();
        user.setName(
"TEST_BLUEDAVY");
        user.setPass(
"JERRY");
        dao.save(user);
        service
=new UserServiceImpl();
        service.setDao(dao);
    }


    
protected void tearDown() throws Exception {
        
super.tearDown();
        dao.delete(user);
    }

    
    
public void testWhenNameAndPassAreNull(){
        assertEquals(
false,service.login(user.getName(),user.getPass()));
    }


    
public void testWhenNameOrPassIsNull(){
        assertEquals(
false,service.login(user.getName(),user.getPass()));
    }

    
    
public void testWhenNameAndPassAreCorrect(){
        assertEquals(
true,service.login(user.getName(),user.getPass()));
    }

    
    
public void testWhenNameOrPassIsError(){
        
try{
            service.login(user.getName(),user.getPass());
        }

        
catch(Exception e){
            assertEquals(AuthronizedException.
class,e.getClass());
        }

    }

    
}

在编写完测试类后就可以开始编写实现代码了,实现代码在编写的时候很简单,只要能够保证测试通过就完事,在测试通过后可以开始考虑重构的事,重构仍然只要保证测试通过即可,其实这个时候就可以看到,简单设计就变得可行了,因为可以通过重构来提升设计。
如果将来这段代码出现bug,就把bug中的输入情况也编写为一个测试方法进行测试,开始运行就应该和bug一样出现问题,这时只需去修正实现代码,直到测试通过为止,那么bug也就自然被修正了。
简单的单元测试的编写较为简单,复杂的单元测试则可能需要使用Mock来模拟一些环境,Mock方面的工具有很多,大家可以去参考相关的开源工具的网站。    

经验总结
对于单元测试通常很多人都有疑问,执行起来的时候经常是不够彻底,特别是在项目时间紧张的情况下,总是觉得编写测试是一种耽误时间的事,其实编写单元测试会为你节省非常多的时间,想想我们大部分的项目都是在集成、修改bug和维护上消耗了大量的时间,既然单元测试这么好,那么我们就实现单元测试吧。
在单元测试中最重要的注意点就是不要依赖于正常的运行数据,所有的数据都要通过代码模拟出来,在测试完毕后清除,避免造成测试对于运行数据的依赖,同时也避免测试数据对于实际运行系统的影响。

posted on 2006-01-22 23:43 BlueDavy 阅读(1919) 评论(3)  编辑  收藏 所属分类: Java软件工程

评论

# re: 既然认为它是好的,就要发挥到极限-系列之二单元测试 2006-01-23 15:18 pesome

呵呵,咋不上msn了,我现在也在做个项目,有些体会,大家交流  回复  更多评论   

# re: 既然认为它是好的,就要发挥到极限-系列之二单元测试 2006-01-23 19:46 Jet Geng

楼主好努力啊。佩服。  回复  更多评论   

# re: 既然认为它是好的,就要发挥到极限-系列之二单元测试 2006-07-17 10:31 伟大的流氓

nn个熊,羡慕你的blog,收藏,以后就在这里混了  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

公告

 









feedsky
抓虾
google reader
鲜果

导航

<2006年1月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

随笔分类

随笔档案

文章档案

Blogger's

搜索

最新评论

阅读排行榜

评论排行榜