读《推行TDD的思考》有感

2014-11-26 12:20

读到同事张逸的一篇博文:推行TDD的思考,我也有感而发。
我刚加入ThoughtWorks时,工作在一个合作开发的Android项目上,我们除了要TDD外,也要求并指导客户公司的开发人员进行TDD,项目开发了快半年,测试覆盖率稳定在90%以上。
业余时间,我主持过GDCR和Coding Dojo等活动。不管在企业还是社区,我见到很多开发者对TDD的理解还停留在字面上。
我认为推行TDD主要在两个方面努力,即意识和能力(好像任何事情都是这两方面哈)。

意识方面主要是:

  1. 认识到自己原有编程方法的不足;
  2. 搞清楚TDD的价值所在,如何弥补原有方法的不足;
  3. 心态开放,勇于尝试新鲜事物,不要浅尝辄止,要持续改进。

意识和能力没有先后关系,而是在不断学习和实践过程中同时提高。在这篇文章中,我更想谈的是能力。
我见到初接触TDD的人常犯下面的错误:

  1. 在声明测试方法后,便开始写实现代码;
  2. 写完“所有”的测试代码才开始写实现;
  3. 一次实现过多的代码(超出当前测试覆盖的业务);
  4. 从不重构;

TDD真是看起来容易,做起来难。

上面这个图一目了然,但其实每一步都是对能力有要求的:

测试先行并不是说不需要思考,直接开始写代码。在开始写代码之前要进行需求分析,将需求分解为任务列表,再从列表中挑选一个任务,转换成一组测试用例,然后不断循环去实现。测试代码其实是产品代码的“用户”,在写测试代码时你就要考虑如何“使用”产品代码,是一个实例方法还是一个类方法,是从构造函数传参还是从方法调用传参,方法的命名,返回值等。这时其实我们就是在做设计,而且设计以代码来体现,比在脑袋中空想要更直观。很多人不懂“意图式编程”,总是习惯先实现一个东西,再去调用它。而测试先行就要求先使用,再实现。这样能少走很多弯路,减少返工。

绿

以最快的速度让测试变绿,意味着我们通常用最直接但可能并不优雅的方式,比如复制代码。然后小步重构,直到符合简单设计的原则:

  1. 通过所有测试
  2. 每个概念都被清楚地表达
  3. 没有重复
  4. 没有多余的东西

难的是要让实现刚好满足当前的测试,不做过度的设计,不写多余的代码。因为如果你写多了,除了引入复杂性以外,多的那部分就没有测试能覆盖到。或者你后面的测试写出来就能直接变绿,你就没办法按TDD的节奏进行下去了。

重构

首先要能识别坏味道,一些低级的Smell,很容易识别,比如:Magic Number,重复代码,太大的类,太长的方法,命名等。但更高级的如Feature EnvyLazy Class等就比较难以识别。

只要识别到Smell,知道用什么手法去重构,剩下的就比较简单了,现代的IDE,尤其是JetBrains的产品,对重构的支持非常强大,几乎都可以用快捷键完成。选对工具非常重要,善假于物能极大提高开发效率。但也不能过分依赖工具,要明白每一个手法背后的原理。所以推荐每个想要实践TDD的开发者,一定要先读《重构》。

最后我想说:
TDD不是银弹,不可能适合所有的场景,但这不应该成为我们拒绝它的理由。
也不要轻易否定TDD,如果要否定,起码要在认真实践过之后。