• 2008-01-20

    实践测试驱动开发

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/14189175.html

    作为一个有理想、有追求的程序员,你成天被各种名词包围着,你对其中一个叫做敏捷的东西特别感兴趣,因为它特别强调人的作用,这听着都让做程序员的你感到舒服。为了让自己早日敏捷起来,你从众多的敏捷实践中选择了一个叫做测试驱动开发(Test Driven Development,TDD)的作为你的起始点。因为它对你周遭的环境要求是最低的:它不像结对那样,要求其他人和你一起合作;也不像采用Story那样改变你所在团队的做事方式……你所需要做的,只是在你编写业务代码之前,把测试先写好。这完全是一种润物细无声的做法,根本无需告诉你之外的任何人。就在别人忙碌的找bug时,你便开始享受敏捷带给你的快乐了。顺便带来的好处是,下次在那里和别人争论敏捷的时候,你可以以一个实践者的姿态出现,而不是在那里信口开河。

    你不会打无准备之仗,于是,你通读了Kent Beck的那本薄册子。通读之下,你对TDD更是充满了信心。因为“红——绿——重构”的步骤实在是简单得令人发指。好吧!总而言之,你已经信心十足的准备开始TDD,步入敏捷的康庄大道了。

    理想很美好,现实很残酷。

    当你着手在实际项目中体验TDD的时候,一切变得并不像最初看起来的那样美好。虽然你努力的坚持着TDD的原则,但你经常就会发现某些东西不好测,比如你遇到了数据库,比如你遇到了GUI,比如你遇到了计时器(Timer)。敏捷并非教条,当某些事不可为的时候,你完全可以不那么坚持。于是,你告诉自己,不好测的东西可以不测,这样,至少从心理上来说,你觉得舒服多了。随着工作的继续,你发现,你不能测的东西越来越多,单元测试的覆盖率随着开发的进行正在逐渐降低,一丝恐惧涌上心头。回过头来,再去看Kent Beck的书,你突然觉得,你似乎被骗了,因为Kent Beck的例子貌似全都是逻辑,如果只是逻辑,当然好测了,但现实从来就不是这样。

    难道TDD只是看上去很美?

    显然,你不愿意就这样放弃,放弃你苦心学来的软件开发秘籍,那些传说中的高手极力推崇的TDD必然有一定道理,TDD确实能够让你感觉很好:能测试的那部分代码确实极大的增强了你对软件质量的信心,而且出错了也确实好找,每次修改代码之后运行测试出现的绿条也确实让你身心愉悦。

    那问题到底出在哪呢?你陷入了沉思。

    信马由缰,你翻开了自己写过的代码。看着自己写的这些代码,你忽然意识到一个问题,自己遇到的问题并不属于TDD,而是属于单元测试。正如你之前所想到的那样,TDD做法本身的结果是让你感到快乐的。对,一定是单元测试本身出了问题。那单元测试出了什么问题,很显然,一大堆不能测试的部分让单元测试变得很难写,降低了单元测试的覆盖度。那是不是这会是一个无解的问题呢?你显然不愿意就此放弃,所以,顺着这个思路继续向前。

    TDD之所以让你安心,主要是每次编写代码之后,运行测试会出现一个绿条,告诉你测试通过。这样,你可以放心大胆的向前继续,因为你的代码并没有破坏任何东西。究竟是什么让你感到不安,显然是那些测试没有覆盖到的代码。你又仔细翻看了一下那些没有测试覆盖的代码,你的思路一下子清晰起来。之所以这部分让你不安,因为里面除了那些确实不好测试的部分之外,里面还有一些逻辑。如果只是那些真正不好测试的部分没有被测试覆盖到,你会觉得心里还有一些安慰。你确定了,真正使你不安的就是与不好测试的代码共存亡的这些逻辑部分。

    如果测试可以覆盖到这些逻辑的部分,至少从感情上来说,就可以接受了。那怎么才能让这些部分被测试覆盖到呢?你仔细观察着那些没有测试的代码,如果这样做,这个部分就可以测试了,如果那样做,那个部分也可以测试了,一来二去,这些貌似不可测试的代码可以分解出许多可以测试的部分。

    你的心情一下子好了许多,因为这么做终于可以让测试的覆盖度达到让你心理上可以接受的范围。不过,新的问题也随之而来。我在做什么?拆来分去,这不就是设计吗?怎么走到这里来了。我不是在分析单元测试的问题吗?对了,我最初的问题是TDD,怎么一路跑到设计上来了?

    TDD?设计?

    你突然发觉自己对TDD的理解有一些偏差。TDD,并不代表不需要设计。读过很多书的你突然想起了Robert Martin那本著名的《敏捷软件开发》,上面有一个关于数据库访问的例子。那个例子里面,前后两个版本的差异正好就是考虑设计的结果。通常,在设计中考虑测试,会很容易找到设计中僵硬的部分,让程序更加灵活。再进一步,如果在开始动手之前,稍微进行一些设计,这些问题还是可能注意得到的。你突然觉得,正是因为TDD本身过于强调测试的价值所在,让你忽略软件开发中很重要的部分:设计。

    思路一下子清楚起来,TDD其实不只是“红——绿——重构”,它还是与设计相关的:在动手之前,还是要有一定的设计,而且,在设计中要考虑测试的问题。终于解开了心中的困惑,现在的你,对于TDD有了一个新的认识,虽然这个认识不见得是什么终极真理,但至少是通过自己的思考得来的,这让你更加相信实践出真知的道理。

    理清思路后,你更加坚信TDD本身的价值所在,也坚定了在日后开发中继续使用TDD的念头,当然,目光远大的你已经盯上了其它的敏捷实践。

    分享到:

    历史上的今天:

    引用地址:

    评论

  • 很好的文章~~~~
  • dreamhead回复深秋小雨说:
    没有逻辑的东西为什么要测试呢?
    ===================
    嗯。。我觉得只是为了驱动开发吧:我需要这么个方法来保存我的blog。如果不写测试,如何凭空产生出这个save方法?也许以后需求会变复杂,那时候可能就需要做些逻辑判断了。
    不知道我对TDD的理解有没有偏差。
  • 仔细想了很久,我保存博客的方法没有什么逻辑,就只是简单的调用DAO来持久化,所以只要保证它能够运行完save方法不出错就行了。而且,这只是对需求的描述:我要写一篇博客,写了标题,内容,设置了博客的分类,上传附件,保存创建时间,最后保存到数据库——一点业务逻辑都没有,所以我感觉没什么assert好写。

    最多。。可能我需要Mock一个DAO,然后verify是否这个DAO的save方法被调用了。

    这样对吗?
    回复深秋小雨说:
    没有逻辑的东西为什么要测试呢?
    2008-07-18 08:39:41
  • 我理解中的TDD是这样的,不知道对不对:

    我举个例子说吧,比如说,我要做一个Blog系统。很早以前,我会先设计数据库表结构。后来,我开始试着先写Service层的接口。如果用上TDD的开发方式,是不是就是针对Service的接口写测试??当然接口刚开始是不存在的,我先写一个测试用例,代码大概是这样的:

    private BlogService blogService;

    public void testSaveBlog(){
    Blog blog = new Blog();

    blog.setTitle("title");
    //setContent,setCreatedTime.......

    blogService.save(blog);
    }

    然后为了让它编译通过,我得开始写BlogService接口和Blog,写Blog这个Entity要根据测试代码里调用的各个set方法来写。为了让测试通过,又要写BlogService的实现类,实现这个saveBlog方法,虽然这个test方法里没有什么逻辑,也没用到assert判断,但其实这时候就是在设计领域模型了,是这样么?
    回复深秋小雨说:
    没有assert,那预期是什么呢?没有预期,那怎么做都是对的。

    我一般做的时候,会从先模型下手。
    2008-07-16 08:38:27
  • 与博主有相同的感受,但还没有到达博主最后叙述的境界
  • 不会这么巧吧!
    那再问一下,博主在大学是否有姓"省"的同学^_^
    回复疑似故人说:
    呵呵,有啊!
    2008-04-30 15:31:37
  • 冒昧问一句,博主不会是姓郑吧?
    回复疑似故人说:
    貌似是的。
    2008-04-29 23:00:50
  • 还是分层,模块化的在TDD中的作用,楼主写这么多打字有点儿累啊:)
  • 如果是UI程序呢?怎么做TDD。这个就困难了吧。因为你还是需要鼠标点阿点的。其实我现在就很想学学TDD。赫赫。另外,你的邮箱可以告诉我吗?我还有些事情想咨询下。请放心,不会占用你太多时间的。:)
    回复zw说:
    这个思路是有问题的。一个设计良好的程序,其核心部分应该与UI展现无关,所以,也就是正常做单元测试的方法。单单UI部分,有很多功能测试工具可以用。不能测试,通常意味着设计存在某些问题。

    我的邮箱就在这个blog上可以找到。
    2008-04-11 00:06:04
  • 写的不错,理解了TDD的精髓所在
    希望有更多的人使用TDD
  • 文章不错,很有启发。不过我一直在想TDD实在是一件很高级的事情。对一个人的个人素质和综合能力要求很高。TDD要求在写代码之前首先你要很详细的了解需求,然后进行一些总体设计,再开始写测试,这种测试的编写对一个人的要求很高。写的不好有可能会影响整个项目。
    回复海阳说:
    TDD,要求的了解需求,并不是要一口气掌握所有的需求,而是把握好当前的需求。其实,TDD要求的这些基本素质,比如理解需求,比如设计能力,即便你做的不是TDD,也应该同样具备。所以,从这个角度来说,我并不觉得TDD比其他的方法要求更高。

    换个角度来说,对于个人来说,如果一种方法对人要求高,而你又可以自如的运用它,那证明,你已经是个高素质的人了。
    2008-02-22 16:54:22
  • ThoughtWorks真是家好公司,果然每个员工都是很有想法的。
  • 哈哈