TDD的全称是Test Driver Development,测试驱动开发。就是开发要以测试为驱动。编码之前,测试先行。代码都没有,我如何测试,我连要测的对象都没有啊?这好像是个问题。
TDD的哲学为我们解答了这个问题:先编写测试用例(没有代码之前这些测试用例一个也通不过),然后再写代码让这些测试用例通过。更进一层的讲就是:编写足够的测试用例使测试失败,编写足够的代码是测试成功。我们编码的目的更加明确的。
TDD是大名鼎鼎的极限编程的一个最重要的设计工具之一(另一个是重构(Refactoring),这是它的荣誉,下面我列举几点它实际的好处。
TDD带来的好处有:
1. 你会更加站在用户的角度去看你将要完成的产品,你要尽可能想到用户所有进行的操作。而不是从程序员的角度想用户应该会如何去使用我们的产品。
2. 测试用例是在对功能进行测试。在写代码之前先写测试用例,可以对我们编写代码提供指导性的参考。防止我们漏掉一些功能。
3. 它使我们对自己代码有了信心,因为我们事先设计好的所有测试用例都Pass了。
4. 如果在更改代码后测试用例不能通过,我们可以根据不能通过的测试用例精确的定位问题,并轻而易举的解决的这个bug。
5. 哈!我们的一整套完备的测试用例在这里替我们把关(把的什么关?),我们就可以十分安全的使用极限编程的另一个最重要的工具——重构。重构改变的是代码的内部结构,而不会改变外部接口功能。知道在做重构时测试用例是把的什么关了吧!很明显,测试用例是保证我们在进行重构时,不会影响到代码的外部接口功能。所以我刚刚说,我们进行的重构是十分安全的。
6. 基于第5点,我们找到了重构的信心,必要时候你还可以痛痛快快的并且满怀信心的对代码做一场大的变革。这样我们的代码变得干净了,扩展性、可以维护性以及易理解性纷至沓来。
TDD有这么多好处,但它也不是免费的午餐,它需要我们有设计完备的测试用例的能力(这项能力是长期理论与实践的结合体),否则你将会吃了亏编写了一大堆测试用例,却没测到点子上。可怕的是,你还对你“测试通过”的糟糕的代码满怀信心。
TDD的主旨是高效,可能这个高效不是非常高的开发速度。 通常的软件开发过程是先写功能,然后再写测试。甚至有时候只进行某些方面的测试,或者有省事的就略去了某些细小功能的测试,或者是忘了对这些模块的测试。 这些看起来对软件影响不大,但是似乎有点过于自信了。因为我们是人,不是神,所以难免会出一些这样或那样的错误,而这些小的问题在项目整合起来以后进行排错是非常令人头疼的。 TDD的思想是以测试推动开发进程。因为我们在软件开发之前,每个程序单元的功能都已经确定了。程序员在理解完整个程序需求以后,直接进行开发,有可能会因为种种原因考虑不很周全,似乎功能实现的没有问题了,但是其中却可能隐藏着非常可怕的Bug。TDD促使开发人员先根据程序单元的功能编写测试代码,就像是先建一个模型,然后向里面浇注合适功能的代码。最后满足所有的测试验证了,才能正常通过测试,这个程序单元才算完成。 这样消除了开发人员主观性的对程序单元健壮性的评估,更客观的验证每一个程序单元的功能实现以及可能出现的Bug。 当然,这些操作都需要有大量的代码支持,所以费事是在所难免的,但是这点“费事”与健壮性非常强的代码相比,有些人还是偏向于使用TDD。 |
首先站在客户方代码的立场,可以获得更好的api。
其次可以改善软件质量,支持重构。
其三,大幅减少debug时间。
前期投入大,后期能大幅缩短开销,将问题发现在最源头
提供明确的目标:
你很清楚,一旦结束(测试通过),你的工作就完成了(假设你的测试写的很好). 测试代码会为代码建立一个自然的边界,使你把重点集中在当前任务上. 一旦测试通过,就有确切的证据证明你的代码能工作. 相对于人工的测试用户界面或者比较日志文件中的结果,在一个xUnit框架中运行自动化测试,速度要快几个数量级. 大多数xUnit测试的运行只需几微秒,而且大多数采用TDD的人都会一天运行数次测试. 在许多开发小组中,将代码上传配置库前,必须成功地通过测试.
提供文档
你是不是经常遇到看不懂的代码? 这些代码可能没有任何文档说明,而且开发代码的人可能早就走了(或者去度假了). 当然看到这种代码的时间往往也很不合时宜,可能是凌晨3点,也可能有位副总在你旁边大声催促着赶快解决问题,这样要想花些时间来愿作者的意图就更困难了. 我们见过一些好的单元测试文档,它们会指出系统要做什么. 测试就像原开发人员留下的记号,可以展示他们的类具体是怎么工作的.
改善设计
编写测试能改善设计. 测试有助于你从界面的角度思考,测试框架也是代码的客户. 测试能让你考虑得更简单. 如果你确实遵循了"尽量简单而且行之有效"的原则,就不会写出篇幅达几页的复杂算法. 要测试的代码通常依赖性更低,而且相互之间没有紧密的联系,因为这样测试起来更容易. 当然,还有一个额外的作用,修改起来也会更容易!
鼓励重构
利用一套健壮的测试集,你能根据需要进行重构. 你是不是经常遇到一些不知是否该修改的代码? 种种的顾虑让你行动迟缓,过于保守,因为你不能保证所做的修改会不会破坏系统. 如果有一套好的单元测试集,就能放心的进行重构,同时能保证你的代码依然简洁.
提高速度
编写这么多测试会不会使开发速度减慢呢? 人们经常会以速度(或开发开销)作为反对进行TDD和使用xUnit框架的理由. 所有的新的工具都会有学习曲线,但是一旦开发人员适应了他们选择的框架(通常只需要很短的时间),开发速度实际上会加快. 一个完备的单元测试集提供了一种方法对系统完成回归测试,这说明,增加一个新特性之后,你不必因为怀疑它会不会破坏原系统而寝食难安.
提供反馈
单元测试还有一个经常被忽略的优点,即开发的节奏. 尽管看上去好像无关紧要,但通过测试之后你会有一种完成任务的成就感! 你不会成天地修改代码而没有任何反馈,这种测试-代码-测试的方法会鼓励你动作幅度小一些 通常修改一次代码的时间仅仅几分钟而已. 这样你不会一下子看到冒出一大堆新的特性,而只是让代码每次前进一小步.
TDD所带来的好处是否被过度的夸大?
当需要进行测试时,我信守下面的经验主义的做法:
- “先测试”还是“后测试”并不重要,只要你是在测试。
- 在你的开发过程中尽可能早的考虑测试。
- 不要让某个框框限制了你的行动。例如,不要轻信那些人告诉你的、要写出“尽可能简单的能够运行的程序”—也就是所谓的YAGNI—的话。如果你的经验告诉你,未来你会用到这个额外的类—虽然现在用不着,你应该相信你的判断,加上这个类。
- 记住,功能测试是真正对用户有意义的测试。单元测试只是为你—开发者—服务的。属于奢侈品。如果你有时间去写单元测试,那最好了:当你的程序出现问题时,它们能帮助你省去很多时间。但如果你没有时间,你要确保功能测试能覆盖到你的产品里用户所期望的所有功能点。
- 如果你没有做驱动测试开发,不要有任何的不安。有太多的因素都能导致这种开发方法在众多的项目和个人开发习惯中水土不服(有很多因素那些TDD极端主义者们永远都不会提)。
本文主要是基于本人的开发经验,概叙一下TDD,也就是测试驱动开发。我比较喜欢用问题方式来写,语言水平有限 希望读者看得懂且有帮助
TDD这个东西 你一般用了之后会上瘾:) 它可能改变你以后的编程习惯
什么是TDD
故名思意就是用测试的方法驱动开发。简单说就是先写测试代码,再写开发代码,和传统的方式是反的。
为什么要用TDD
用TDD的方法可以使代码干净(代码重构的结果),测试覆盖率高(先写测试的结果),软件做集成测试的时候一般问题会比较少。
而且你敢改人家的代码,看到有fail的test case 证明你有改错人家的东西,看到所有的test case都过了的话,你也很有信心说,我没有改错,或程序不会因为我的改动而挂掉。
什么时候TDD
TDD是在Unit Test,也就是单元测试时用的方法。
什么地方TDD
我觉得写任何代码都可以用TDD吧
怎么做TDD(关键5步)
- 加入一个新的测试
- 运行下新加的测试,看到它失败(因为你还没写功能代码)
- 对开发代码做很小的修改,目的就是让新加的测试通过 (注意这里的目的)
- 运行所有的测试(test case),然后看到所有测试都通过了 (看到测试都变成绿色,一般都会小开心一下)
- 移掉重复的代码,对代码进行重构 (既包括功能代码,也包括测试代码。特别注意红色的字串 一般会有重复,还有一些代码可以抽出来变成公用方法,测试代码中同样的初始化和还原测试环境的代码,可以放到intilize和cleanup中去)
而外还有一些步骤也是可以加入的,比方
- 在写测试代码前,先从需求出发,准备一个Test list (需要测到的功能的列表)。忘掉你该怎么实现,那是后面的事情
- 每测完一个就用横线划掉
- 如果发现有漏掉的test 就加到这个列表中(列表测完你的功能也就完成了)
TDD的好处,和不足的地方
好处
- 测试代码都是从客户需求出发的,不是重实现出发的。测试更关注于对外部的接口。
- 软件的需求都被测试代码描叙得很清楚,可以减少很多不必要的文档(有些时候写文档时间比开发时间多多了,需要一个专门写文档的,而且用的机会很少。这里我很喜欢敏捷开发中说的:Just enough)
- 每次都是很小的步骤,这样你就很集中注意解决一个问题。葛优说的:步子迈大了容易扯着蛋,步子大想的就多,容易忽视些东西, 容易出错。小而简单就是美
- 可以优化设计。如果有做过测试驱动开发的会发现,为了更好的,更容易的做单元测试。它逼着你面向接口编程和使用一些设计模式,自然设计就灵活了,耦合性也低
缺点
怎么学习TDD最好
我觉得最好且最快的方式就是 XP中提到的结对编程,一个有TDD经验的坐在"后面",指导另一个不大熟悉的人,两人一起来完成一个类或模块的功能
几个关键点
- 记得你是做单元测试,不是集成测试,你要测得仅仅是你的类的功能,不要去测别人类的功能,一定要知道测到什么程度就好了,剩下的可能是别人需要测的
- 每次都是一小步,目的只是用最简单的方法让新加的test case过掉而已
- 在改/加任何功能代码前,一定要先想是不是要改或加test case。
- 测试驱动产生的单元测试代码是代替不了集成测试的,它还是单元测试
- 测完记得清理测试环境,还原到测试之前的样子
后面的文章我准备用VS2008来举简单的例子,还有一些测试的模式,测试的辅助工具...
关于TDD的观点:质量是反复思考的结果,仅靠解决Bug无法获得
“通过单元测试可以改善代码质量”这一观点已经得到广泛认可。培训师、顾问兼咨询师Michael Feathers在最近的帖子中对其提出了质疑。他谈及单元测试、集成测试、TDD和净室软件开发(Clean Room Software Development),认为代码质量是反复思考的结果,仅靠解决bug无法获得。M3P的独立咨询师Steve Freeman进一步阐述了Michael的观点,从“认知方面对TDD进行了分析”,并解释了TDD的好处从何而来。
Michael认为,利用测试找Bug来改善质量的想法是有不足之处的:
对于单元测试,最常见的理论就是“通过解决测试找到的bug,软件的质量得以提升”。乍听起来,好象没错儿。测试有通过或失败两种结果,一旦失败,我们就知道肯定是有什么地方出了问题,就要修复它。假如你赞同这个说法,你就会预期在做集成测试时发现更少的集成错误,在做单元测试时也是一样。这个观点貌似挺好,不过它是错误的。能够证明这一点的最佳方法就是:将单元测试与另一种提高质量的方法相比较,而且这种方法有着很好的度量效果。
为了证明他的观点,Michael提到净室软件开发(Clean Room Software Development),这是一种在上个世纪八十年代曾被使用的开发方法。根据Michael所述,净室方法不包括任何单元测试过程:
净室方法背后的观念,就是要通过更严格的开发纪律来提高质量;在净室中,你要为每一小短代码写逻辑断言;在代码复查时,你还要证明代码的功能和你写的断言完全符合,功能不多也不少。这种方法非常严格,甚至比我所说还要极端,因为净室方法的另一个宗旨是:不能有单元测试。一个都没有。毫无必要。代码开发完成之后,只要被复查过就被认为是完全正确的。唯一的测试就是在功能层面的随机测试。
使用净室方法的结果很有趣:不经任何单元测试,代码质量也提高了。净室方法和TDD方法的相似之处在于:迫使开发人员不断复查、重构并改进他的代码。Michael的结论就是:
我认为,我们不能机械地看待测试。单元测试并不能在“单元”这个层次上通过抓到错误来改进质量。而且,集成测试也不能在“集成”这个层次上通过抓到错误来提高质量。实际上背后的道理难以言说。质量是反复思考的结果——严谨的反复思考。这就是关键。强化纪律的技术一定会提高质量。
从Michael的帖子出发,M3P的独立咨询师Steve Freeman进一步阐述了这个观点,并谈到“对TDD在认知上的分析”。开发人员的某些决定会影响他们的代码:
事实证明,人们并没有真正花时间从各种可行的方案中进行权衡,而是使用了“首先契合(first-fit)”的方法,即将已知的解决方案按序排列,然后选择第一个看上去最好的方案。所有这些都是在潜意识下发生的。在这之后,我们迟钝的理性思维才会想办法证明这个已成事实的决定——我们甚至都不知道这些是如何发生的。
关键在于不要直接采纳第一个在我们脑海里出现的解决方案,而是要评估不同的可选方案,这就是Steve认为TDD有益的原因:
测试驱动开发打破“首先契合”这种模式匹配,从而发挥了(或应该发挥)作用。通过TDD,我们不再强制用脑海中出现的第一方案来解决问题。我们也不得不远离自己的“安全地带(comfort zone)”来考虑真正需要实现的需求。更重要的是,从“写一个测试开始”迫使我们首先去想真正需要的是什么(要测试什么),然后再用我们专家般敏锐的大脑来得出解决方案。
Steve举了一个例子来证明他的观点:
最有力的证据就是Arlo Belshee所在的小组,他们实现了无序结对编程(Promiscuous Pairing)。他们从经验中发现:相对于成员自己决定结对时间,强制他们每隔几个小时就切换结对,这样的生产率最高。他们认为,这样可以保持每个人处于“初学者”的心态,也就可以像“初学者”那样思考。
争论TDD
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://dreamhead.blogbus.com/logs/63646898.html
咨询的过程中,经常会与人讨论TDD。初时,我会很耐心的跟人解释TDD的种种好处,试图让他们能够皈依TDD,结果总不尽如人意,争论者总是可以抛出一些新的观点,逼迫着我重新认识TDD,甚至最低落时,我会怀疑TDD是不是真的有效。
通过反思,我发现,所有和我们直接在一起写代码的人,都没有质疑过TDD,而且一旦真正进入节奏,便乐此不疲。而与我们争论的人中,为数不少的对于TDD,或是浅尝辄止,或是压根没有碰过。换句话说,他们的问题都是“想”出来的。真正做起来是什么样子,他们自己也不知道。
网上的某些讨论也有着这种倾向,想出一个问题吓唬自己,还试图让别人认同自己,比如这个。对于这些人,着实没有必要浪费口舌。连电都没有,我怎么给你解释灯的都好处都是白搭的。况且,欲加之罪,何患无辞!
争论的人里面,有些人是实践过的,但是,结果很糟糕。我们的沟通常常是这样:
我:你怎么理解TDD?
对方:不就是先写测试后写代码吗?
原来TDD也可以没有重构啊!打开他们的测试,几屏都翻不完的测试,让人目眩神晕。
我:这么长的测试你怎么保证它的正确性呢?
对方:哦?没有想过。
测试错了,但无法证明究竟是测试写错了,还是代码写错了,我合作过的不少团队都出现过这样的情况。
问题到底出在哪?我想了很久。
通过与不同的团队的合作,我逐渐意识到,这些团队欠缺的根本不是TDD,他们压根不知道如何写好代码。对于这些人而言,即便他们知道TDD有着“红——绿——重构”的节奏,也不知道代码应该重构成什么样子。在他们眼里,重构和不重构,没有任何差别。
在他们原有的认识中,写代码就是完成功能。在这样的背景下,很多人每天都忙碌于在原有的代码上堆叠更多的代码。在合作团队里,有不少人工作才一两年,更有甚者,是工作后才开始写代码,他们对于写代码的认知仅仅停留在照猫画虎的阶段,甚至连最基本的编程技巧都还不了解。更可怕的是,他们的代码风格还在不断被后来者模仿。对于这些人来说,如果不改变他们对代码的认知,指望“所谓的TDD”改变一切无异于痴人说梦。
想TDD吗?先学学怎么写好代码吧!
实际经验证明:TDD可以提高软件质量
在Empirical Software Engineering杂志上首次发表的一篇研究报告声称:“看来TDD可以应用在多个领域中,并显著降低软件的缺陷密度,同时也不会明显降低开发团队的工作效率。”研究对比了4个在微软和IBM执行的项目,这些项目使用了TDD方式开发,并与没有使用TDD开发的类似项目进行了对比。
研究报告的作者包括:来自微软的Nachi Nagappan和Thirumalesh Bhat 、IBM的E. Michael Maximilien,以及北卡罗来纳大学的Laurie Williams,并发布在Empirical Software Engineering杂志的第13卷第3期上。读者也可以在微软研究院的Empirical Software Engineering Group中找到该报告。
报告中研究的4个案例,1个来自IBM,3个来自微软。每个案例研究都对比了开发同一个产品的两个团队,他们使用同样的开发语言和技术,处于同一级别的管理之下,唯一不同之处在于:一个团队使用TDD,另一个不用。在开发过程中,这些团队都不知道自己处于研究之下。IBM案例中的团队在开发设备驱动程序,微软的团队在开发Windows、MSN和Visual Studio的相关应用。
报告中讲述了团队使用的TDD实践,包括分钟级别的工作流程(minute-to-minute workflow)和任务级别的工作流程(task-level workflow)。
分钟级别的工作流程如下:
- 编写一些新的小单元测试用例
- 运行测试,看着它们失败
- 编写实现代码以满足测试
- 重新运行单元测试用例,确保它们现在可以通过
任务级别的工作流程是这样子的:
缺陷密度是通过每千行代码的缺陷数目来衡量的。相较于不使用TDD的项目而言,这四个产品在发布前的缺陷密度降低了40%到90%。从团队管理层的主观报告看来,在开发的初始阶段,使用TDD的团队要多花15%到35%的时间;不过团队都同意一点:这个时间被减少的产品维护时间抵消掉了。
报告中的结果可以与Maria Siniaalto在2006年发布的一篇文章相对比。该文章中试图复审并总结其他13项有关TDD的研究,这些研究包括在纯商业背景、半商业背景和学院背景中进行的研究。在文章的结论中,作者写道:
基于这些现有研究的发现,可以得出结论:TDD看来可以提升软件质量,特别是在纯商业背景中。对于半商业背景或学院环境中的研究来说,结论不是那么明显,可这些研究没有提到任何软件质量的降低。TDD给工作效率带来的效果不是十分明显,而且结果会根据研究的环境发生变化。然而,有证据证明:TDD不一定会降低开发人员的工作效率,或是增加项目的交付时间。某些情况下,TDD会带来显著的工作效率提升;同时,在提到的13个案例研究中,只有两个案例指出工作效率降低了,不过这两个案例同时指明质量的提升。
您使用TDD有哪些经验?见到质量的提升了么?对于开发人员的工作效率和开发时间,您看到了哪些影响?请在文后留下您的评论,分享您的经验。
查看英文原文:Empirical Studies Show Test Driven Development Improves Quality
译者郑柯曾任职《程序员》杂志副主编,《项目管理修炼之道》译者。
-
TDD 的测试部分其实是某种形式的回归测试(regression test)。与传统回归测试不同的是,TDD 测试频度更高,周期更短。然而,回归测试不是什么新概念,恐怕 25 年以前就有了,属于传统软件工程的精华。回归测试也是 TDD 中最有价值的部分。
TDD 测试更频繁,更全面,当然能尽早发现错误,减少错误,获得更稳定的软件,从而提高软件质量,这本在意料之中。作者们用实际案例、定量数据的分析验证了大家的推测,正是这篇研究报告的价值所在。
我的观点和问题是:
1、这项研究是否说明 TDD 可以用在非 XP 的过程之下?
2、案例中的 IBM、微软项目团队都是非常成熟的软件工程团队,Windows、MSN 和 Visual Studio 也都是非常成熟的产品架构。
如果 TDD 用在国内的典型项目上,增加的开发时间很可能要更多,大概在 50% 甚至 1 倍以上(我个人的估计)。
我曾经在 Kent Beck 和 Martin Fowler 的作品中看到,采用 TDD 代码之后,单元测试代码常常和实现代码的规模差不多,有 2 万行的实现代码,差不多也有 2 万行的(单元)测试代码。
编写高质量的单元测试,也是需要时间的。我估计国内的客户、开发商在做项目计划的时候,大多都没有把这个时间考虑在内,或低估了设计、编写测试、执行测试所需要的时间。
3、采用 TDD,在带来质量提升的同时,有可能降低开发效率,延长交付时间。当然,如果质量回报大于效率、时间损失,那么就值得尝试 TDD。
4、TDD 是可行的,而且可以提高开发质量,这些都没有问题,那么是否还有比 TDD 效率更高、风险更小、更加灵活、更符合大众习惯的开发方式?
我相信是有的。
资深敏捷 OO 教练 张恂
www.zhangxun.com - @L_502_56@
评论
1.下手之前更清楚自己要干什么
2.在重构的过程中有了一层保险,不至于改了代码,引发了其他地方的bug
-------------
深有同感!一个团队里有工作一两年的新手是正常的,比较恐怖的是,他们不知道正确的方向在哪。当他们在工作年限上成为老手,而编程功力却停留在一两年的新手水平时,团队在他们在支配下,很可能被带入万劫不复的境地。
别人告诉你怎么做可行,你不信
别人说你去试试看,你不试
那你想怎样?
想找个人帮你把项目带了算了?帮不帮你领工资?