写伟大的测试:最好与最坏的实践
原文地址:http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/
这篇博客只针对那些至少有少量单元测试经验的开发员,如果你没有写过单元测试,请先读读这个介绍尝试一下。
好的单元测试和坏的单元测试有什么区别?你怎么学习怎么写一个好的单元测试? 这明显是很遥远的,即便你是一个天才且拥有数十年经验的程序员,你已有的知识和习惯也不会自动的让你写出一个好的单元测试来。因为这和写程序是不一样的编码,并且大部分的人对单元测试要达到的目标有一个没有任何帮助的假设。
我见过的大部分单元测试都是没意义的。我不是谴责开发员:通常来说,他或者她只是被命令开始去写单元测试,所以他安装NUnit然后就开始写测试方法。一旦他看到绿了,他们就假定做的很正确。这是错误的假定!这样非常容易产生一大堆对项目价值很小但是维护成本确极其高的单元测试。你认为这是敏捷吗? 单元测试不是用来寻找bug的
现在,我非常喜欢单元测试,但是只有在你知道单元测试在TDD中的承担的角色,并且没有任何用单元测试来发现bug的错误想法的时候。
以我的经验,单元测试不是一种有效的寻找bug的方法,也不是回归测试中的一种工具。单元测试,按字面意思,就是测试代码中的各个隔离的部分。但是当程序真实的运行的时候,所有的单元要一起工作,整个比把所有单元加起来更复杂。模块X和Y分别工作正常不能代表他们相互兼容或者配置正确。同时,各个独立模块里的bug也不一定会表现在最终用户面前。并且,因为你假设了你的单元测试的前提,它们不会检测到不在你的前提情况下的bug(比如,如果IHttpModule接口有异常的请求)。
所以,如果你想要找bug,把整个程序像产品环境下那样跑起来会有效的多,就像你做手动测试的时候一样。如果将来你把这些测试自动化了,那么它就叫做集成测试,这和单元测试用的是完全不一样的方法。对于这样不同的工作,你不想用更合适的工具来做吗?
(注意:在一种情况下单元测试可以有效的发现bug。那就是当重构代码,并且调整单元测试代码但是没有改变行为的时候。在这种情况下,单元测试通常能发现行为的变化。)
好了,如果单元测试不是用来找bug的,那么用来干什么的呢? 我打赌你肯定听这个答案超过一百次了,但是因为开发人员对单元测试有较深的误解,所以我还是要重复一遍。 就像TDD专家们一直重复说的:“TDD是一个设计流程,不是一个测试流程”一样。让我描述一下:TDD是一种交互式的设计软件模块(单元)的健壮方法,以使模块的行为能够按照单元测试指定的那样运行。
好的单元测试 vs不好的单元测试 TDD帮助你完成按行为设计的独立的模块。好的单元测试是有很大价值的:它记录了你的设计,使重构和扩展更加容易,同事保留对各个模块行为的清晰的概述。 可是,不好的单元测试也很让人不快:它没有清晰的证明任何事情,并且会极大的阻碍你重构或者修改代码。
你的单元测试处于下图中的那个位置?
TDD中创建的单元测试自然的处在最左边。它们包含大量单一单元的行为信息。如果单元的行为改变了,那么单元测试也要跟着变,反之亦然。但是他们不包含任何你的其他代码的假设,所以对其他部分代码的修改不会使单元测试失败(如果失败了,那说不它们不是真正的单元测试)。因此他们很容易维护,并且作为一种开发方法,TDD对任何规模的项目都是适用的。
在图中的另一端,集成测试不知道你的代码怎么被分成了模块,但是它描述了这个系统对用户的行为。它们很容易维护(因为不管你怎么重新组织内部系统,它都不会影响外在表现)并且保证当前系统的功能工作正常。
任何处于它们之间的,都对它要假定或者保证什么混淆不清。重构可能使测试失败,也可能不会,也不知道会不会影响最终用户的体验。改变外部的服务(比如升级DB)可能会也可能不会使测试失败,也许用户功能还继续工作着。任何很小的一个单元内的代码改变都有可能迫使你去修复上百个看起来一样的单元测试,所以他们会消耗大量的维护时间——有时候是实际写代码实际的10倍以上。并且它很令人沮丧,因为你之前做了的假设使这些测试即便通过也不能保证任何事情能够正常工作。 写好单元测试的建议 讨论完了,是时候提实际意见了。这是一些写处于图中极点A的单元测试的指导。 让每个单元测试与其他单元测试正交(独立) o别做不必要的断言 o一次只测试一个代码 oMOCK所有外部的服务和静态数据 o避免不必要的先决条件 单元测试不能用来测试程序配置 从个人的角度,我建议使用像ASP.NET MVC里的fitler自来的东西来做配置。[Authorize]或者 [RequiresSsl] 之类的filter被可选的配置在代码里。配置对集成测试有用,但是对单元测试没任何意义。它不帮助你做更好的设计,也不会检测任何程序问题。 保持你的单元测试命名清楚且一致 不要写没有描述的单元测试名字,比如Purchase() 或者 OutOfStock()。如果你不知道单元测试要干什么,维护起来成本是很高的。
总结 不用质疑,单元测试会提高项目的质量。我们行业的很多人主张有任何的单元测试都比没有好,但是我不同意:一个测试可以有很大作用,也可以带来很多烦恼确贡献很少。这依赖于单元测试的质量,而这又依赖于开发员对单元测试的目标和理念能有多了解。 顺便提一下,如果你想学习集成测试(来补充你的单元测试技巧),推荐Watin,Selenium,或者我最近发布的ASP.NET MVC集成测试库.
附两篇国内的讨论: TDD并不是看上去的那么美 虚拟座谈会:TDD有多美 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |