TDD之Unit Test实践

TDD,耳熟能详的字眼,在几年前写VB Com组件的时候其实就开始重视测试的问题,而在TDD名词正式冒出之后自己其实也一直宣称去实践,但回顾下来,以前做TDD的单元测试一方面原因经常是坚持不下来,另一方面原因是没摆正思想,不过现在去回看以前写的单元测试,根本就不能说是TDD,根本就是Class For Test。
TDD,名词没什么值得大解释的,核心思想就是以测试的方式来驱动开发,按照这个思想其实自然就可以引出TDD的实施方法,TDD的目的主要是为了保证类是为了其所赋予的功能、职责而存在,而不是毫无意义的存在,另一方面也是为了保证避免过度设计,还有就是为了将来的重构方便,为了持续集成存在能够有意义,^_^
TDD的实施方法最重要的就是坚持测试驱动开发的方式,那么具体如何操作呢:
1、编写一个类的实现前首先编写测试类。
2、在编写测试类时需要注意几点,要想清楚要测的类提供的是什么功能,输入的参数是什么,输出的是什么,会有哪些异常情况。
3、在想清楚上面的东西后其实就可以对类写单元测试了,对于一个类而言通常都是外部输入的参数决定了其功能的执行以及异常的产生,所以在编写测试方法的时候应该更多的采用诸如testWhen参数是什么()这样的命名,测试中的重点就是当参数为某个值时类执行后的输出是否符合预期,可能这个时候会有疑问产生,就是有些类是没输出的,这个我是不认同的,一个类必然都会有输出,只是有些可能是输出到数据库之类的第三方中,但对于编写测试代码来说,这并不重要,关键是要确定输出的到底是什么,在测试类中只是要去判断其输出是否和预期的一致,在一个测试类中一般都要包含对于类正常情况的测试和否定情况的测试。
4、在编写完对于类的测试后,这个时候开始运行单元测试,肯定是错误的,那么这个时候就开始去编写实现类,不断的补充,直到单元测试通过为止,在单元测试通过后也就意味着这个类就写完了。

写单元测试的好处:
1、对通过了单元测试的代码有信心。
2、重构时不怕重构会产生新的问题。
3、对集成测试有保证。
4、保证了写出来的类是符合需求的。

可能这样说仍然是很悬乎,在这里我以在Java中TDD方式编写一个用户登录类作为例子来讲讲:
1、建立用户登录类。
2、建立用户登录类的单元测试类。
3、思考用户登录类的输入、功能、输出、各种输入以及异常情况。用户登录类的输入为用户名以及用户密码,功能为根据用户名以及用户密码校验用户是否能够登录系统,输出为登录成功与否,输入以及异常情况会有:当用户名以及用户密码都正确的情况、当用户名、用户密码两个中一个不正确的情况、当用户名、用户密码都为null的情况。
4、根据上面的思考,开始编写测试类中的方法,需要测试的主要是输入以及异常情况下类的执行是否符合预期:
      4.1 当用户名以及用户密码都正确的情况
            在这种情况下,期待登录类执行的输出为成功,那么测试类如下:
            public void testWhenUsernameAndPasswordAreCorrect(){
                      用户登录类 action=new 用户登录类();
                      action.setUsername("name");
                      action.setPassword("pass");
                      assertEquals(true,action.execute());
            }
      4.2 当用户名、用户密码两个中一个不正确的情况
            在这种情况下,期待登录类执行的输出为失败,那么测试类如下:
            public void testWhenUsernameOrPasswordIsError(){
                     用户登录类 action=new 用户登录类();
                      action.setUsername("name");
                      action.setPassword("jjfwe");
                      assertEquals(false,action.execute());
            }
            假设这个时候期待的是登录类抛出异常,那么测试类只要改成:
            try{
                      action.execute();
            }
            catch(异常 e){
                      // 判断此异常是否为预期的异常
            }
      4.4 当用户名、用户密码都为null的情况
            在这种情况下,假设期待登录类执行的输出为失败,那么测试类如下:
            public void testWhenUsernameOrPasswordIsError(){
                     用户登录类 action=new 用户登录类();
                      action.setUsername(null);
                      action.setPassword(null);
                      assertEquals(false,action.execute());
            }
5、运行测试类。这个时候运行测试类肯定是报错,因为用户登录类还没有实现,这个时候就开始去用户登录类中编写实现,边写边测试,直到此测试类通过为止,那么用户登录类也就写完了。

上面举的是一个非常简单的例子,但已经可以开始看出部分的好处了:
1、保证了类的功能是被实现的,满足几种情况下,如将来出现新的情况时再补充。
2、类的实现中不管你是采用数据库登录还是LDAP登录(重构),只要测试是能通过的就OK。
3、强迫coder以用户的角度去考虑问题。

对于测试类而言,最重要的就是测试在一定的情况下类的执行是否符合预期,这就OK了,所以其实测试类也不会非常难写,很多人都会觉得写测试类完全是耽误时间,这个想法是不太正确的,即使是一个再优秀、再出色的coder写代码也是会出错的,何必让这种错误都要等到系统集成后才冒出来呢,那个时候付出的代价我也想会远远超过写测试类消耗的那点时间,而且通常直接写实现的话很多时候会出现偏离需求的现象,从大层面去看所有人都明白系统是应该符合需求的,其实对于类同样也是如此,而且当测试类写出来了后,写实现类通常是一件很快的事。
在写测试类时有几点是非常值得注意的:
1、测试类中如需要使用来源于数据库的数据,这时应该保证测试数据和运行数据的分离,不要去依赖系统中真实的运行数据,而是采用测试数据的方法,测试数据要保证在测试类执行完毕后清除,要做好扫尾工作,不要影响到实际的系统。
2、测试代码和运行代码的分开,这个在java project通常做法就是建立src/java、src/test的两种目录,分开存放。
3、在增加新的功能或原有功能发生变化时,首先应该是去编写或修改测试类。
.... 写着写着忘了其他的几点,呵呵
^_^,对于TDD有疑问的话,请不要一味的怀疑,何不先试试呢?

posted on 2005-12-07 20:39 BlueDavy 阅读(1257) 评论(0)  编辑  收藏 所属分类: Java


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


网站导航:
 

公告

 









feedsky
抓虾
google reader
鲜果

导航

<2005年12月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

统计

随笔分类

随笔档案

文章档案

Blogger's

搜索

最新评论

阅读排行榜

评论排行榜