关于 TDD 的观点:质量是反复思考的结果,仅靠解决 Bug 无法获得

  • Abel Avram
  • 乔梁

2008 年 7 月 7 日

话题:敏捷测试语言 & 开发文化 & 方法

通过单元测试可以改善代码质量”这一观点已经得到广泛认可。培训师、顾问兼咨询师 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 Opinion: Quality Is a Function of Thought and Reflection, Not Bug Prevention
敏捷测试语言 & 开发文化 & 方法