最近机缘巧合,又一次读到好几篇关于争论TDD是否还活着,以及它到底好不好用的问题。似乎在几年前DHH那篇檄文后,过一段时间就会听到一次余响。
这也促使我对反思了一下自己对TDD是什么,TDD与Unit Test的关系,TDD实用么等问题的看法。
TDD是什么
可以从它的名字切入:
-
Test —— 测试,可以不是单元测试,可以没有一层层的mock,甚至可以不能作为程序执行,不能自动判断结果……
但是有一条是测试的关键
无二义的成功/失败标准。
换句话说,在给定的上下文中,测试要么成功,要么失败。且项目所有成员对此都有共识。
缺少这点,测试就退化为宽泛的功能规范,接着就会出现“完成了80%”,“已经开发完了,只需要调好一个bug”这样的进度黑洞。 -
Driven —— 驱动有两层含义,一个是严格意义上浮现式设计要求的写测试之前不能写生产代码。如果说奥卡姆剃刀是“如无必要,勿增实体”那么TDD则是“如无测试,勿增实体”。
这一部分也许过于严格,有争议。但是从另一方面来看Driven可以看作是对整个软件发展过程提供保驾护航的作用。就像是司机替我们操心了交通情况,我们就可以在上下班路上解放脑子刷刷朋友圈,读读书什么的。
没有Test提供的快速,持续,统一的反馈,软件的进展很容易就脱轨了。
很多测试用例的要求和具体技术,其实都只是为了这种反馈的目的而设计的。比如说UT和Mock,是为了测试更快速,而不是说这样才是TDD。 -
Development —— 开发,TDD是直接关于开发的,也就是说测试描述的行为要直接体现在可执行的交付物中。
这是我为什么不认同把ATDD与UTDD并举的说法。验收测试的循环中(ATDD)包含着非常多小的TDD循环,就像是程序中的递归或者自相似的分形结构一样。
ATDD是在单元的TDD基础上的自然延伸,而不是割裂的另一个概念。或者这样说,一个小系统的ATDD,放到更大的范围内,可能也只是系统的一颗螺丝钉而已。这时候莫不是要提出AATDD不成?
另一方面,这也是为什么我不理解TDD必须要xxx,yyy配合才能做成的说法。在我看来TDD是非常基础的实践,是它给其它更高层面的技术和理念提供支持,而非相反。
这就像如果没有Git之类的版本管理工具,敏捷开发是不可想象的。但是绝不是说,只有采用了敏捷方法才能用起来Git。
TDD也是类似,归根结底是一种更高效的写代码的技术。比如我就把TDD看作是一种主动debug的手段。它在这方面起到的作用也就和一个调试器这种工具差不多。
是否使用某种工具,是否花时间来学习工具为将来提高效率,应该是程序员为自己作出的决定。
我的两个标准
当然如果用了工具,有时结果也不理想。
那么有两种可能,要么这个工具不好用,或者至少对要解决的问题不好用。要么,使用者还没有完全掌握用法。
我的两个检验标准,来检验TDD是不是真的发挥了作用。
- 团队方面:
- 团队内review代码时,是先看测试还是先看实现代码?
- 你的代码写好要和其他人对接时,他是跑来问你要文档/要你口头解释,还是去看你的接口测试?
- 个人方面:
你接手了一段遗留代码要改一个bug,无文档,无测试,当初开发人员也走了。
你已经跑起程序,重现出了bug,并开始尝试修改代码调整程序行为。
这时你会情不自禁说出哪句话?
- 算了太麻烦了,不用写测试了吧
- 算了太麻烦了,还是先写几个测试吧
网友评论