为什么我不做 TDD? - Shai

22-12-17 banq

编写大量的测试。这怎么可能是坏事呢?

但随着时间的推移,我从另一个角度看待它。我认为它是一个非常有限的工具,有非常具体的用例。它不适合我所建立的项目类型,而且经常阻碍它应该促进的流动过程。

在TDD其中,我们需要定义约束条件,然后解决问题。
这种方法真的比“先解决问题然后验证约束条件”好吗?

这就是“TDD”与“只写好测试覆盖”的核心区别。

1、TDD好处
TDD是一种有趣的方法。它在处理松散类型的语言时特别有用。在这些情况下,TDD是非常好的,因为它填补了一个严格的编译器和linter的角色。

在其他情况下,它也有意义。当我们在构建一个有非常明确的输入和输出的系统时。我在构建课程和教材时遇到过很多这样的情况。在处理真实世界的数据时,当我们有中间件处理数据并以预定的格式输出时,这种情况有时会发生。

我们的想法是用中间的隐藏变量来构建方程。然后,编码就变成了填入方程。在这样的情况下,这是非常方便的。

编码变成了填空。

2、TDD坏处
"测试驱动开发是一种复式记账法。同样的纪律;同样的推理;同样的结果"。- 鲍勃-马丁大叔

我想说的是,测试有点像复式记账法
是的。我们应该有测试。
问题是我们应该在测试的基础上构建我们的代码,还是相反?

这里的答案并不那么简单。

如果我们有一个预先存在的有测试的系统,那么TDD就有了所有的意义。
但是测试一个还没有建立的系统:有一些情况是有意义的,但不像人们想象的那样经常有意义。

TDD的主要主张是 "其设计":

测试实际上是一种系统的设计,然后我们实现这个设计。

这样做的问题是:我们也不能对设计进行调试。在过去,我曾为一家大型日本公司做了一个项目。这家公司有一套最大、最详细的附件设计书。基于这些设计规范,公司建立了成千上万的测试。我们应该用我们的系统通过大量的测试。请注意,大多数甚至不是自动的。

这些测试都有错误:有许多竞争性的实现,但他们都没有发现测试中的错误。
为什么呢?
他们都使用相同的参考实现源代码:这使得代码中的这些错误长期存在,其中一些是严重的性能错误,影响了所有以前的版本。
我们是第一个跳过这一点完成干净实现的团队。

但真正的问题是进展缓慢:
公司无法快速前进。TDD的支持者会很快评论说,一个TDD项目更容易重构,因为测试给了我们一个保证,我们不会有回归的问题。但这适用于事后进行测试的项目。

3、更糟糕的是
TDD在很大程度上关注快速单元测试。在TDD系统上运行缓慢的集成测试或可以通宵运行的长期测试是不现实的。你如何验证大规模系统并能集成到一个主要系统?

在一个理想的世界里,所有的东西都会像乐高积木一样点击到位。但是我并不生活在这样的世界里,集成测试失败得很厉害。
这些是最糟糕的失败,也是最难追踪的错误。
我更希望在单元测试中出现这些故障,这也是为什么我会进行单元测试的原因:Bug很容易修复。但即使有完美的覆盖率,它们也不能正确地测试互连。
我们需要集成测试,它们能发现最可怕的bug。

结果,TDD过度强调了 "不错的 "单元测试,而不是必不可少的集成测试。
是的,你应该两者都有。但我必须有集成测试。那些测试并不适合TDD过程。

4、权利驱动的测试
我写测试的方式是在个案基础上选择的。如果我有一个案例,提前测试是很自然的,我就会使用这种方式。但对于大多数情况,先写代码对我来说似乎更自然。在写测试时,审查覆盖率数字是非常有帮助的,这是我在事后做的事情。

正如我之前提到的,我只检查集成测试的覆盖率。我喜欢单元测试,并监测那里的覆盖率,因为我也希望那里有好的覆盖率。但是对于质量来说,只有集成测试是重要的。一个PR需要单元测试,我不关心我们是否在实现之前就写好了。我们应该判断其结果。

4、糟糕的自动化
当特斯拉在建立他们的Model 3工厂时,他们进入了生产地狱。问题的根源在于他们试图将一切都自动化。帕累托原则完全适用于自动化。有些事情对自动化非常抗拒,使整个过程变得非常糟糕。

其中真正失败的一点是在UI测试中。像Selenium等解决方案在测试Web前端方面取得了巨大的进步。然而,复杂性是巨大的,测试是非常脆弱的。我们最终得到的是难以维护的测试。更糟的是,我们发现UI更难重构,因为我们不想重写测试。

我们可能可以跨越80%的测试功能,但是对于自动化来说,有一个收益递减的点。在这些环境中,TDD是有问题的。功能是容易的,但建立测试变得难以维持。

最后

我不反对TDD,但我不推荐它,而且实际上我不使用它。
当从测试开始有意义时,我可能会这么做,但这不是真正的TDD。我是根据结果来判断代码的。TDD可以提供很好的结果,但往往它过分强调单元测试。从长远来看,集成测试对质量更重要。

自动化是伟大的。直到它停止。有一个点,自动化测试只是没有什么意义。接受这一点,并将我们的努力集中在一个富有成效的方向上,会节省我们大量的时间和精力。

这是我作为一个喜欢类型安全的严格语言的Java开发者的偏见。像JavaScript和Python这样的语言由于其灵活性,可以从大量的测试中受益。因此,TDD在这些环境中更有意义。

总而言之,测试是好的。但TDD并不能带来更好的测试。如果它对你有用的话,这是一个有趣的方法。在某些情况下,它是巨大的。但是,认为TDD是必不可少的,甚至认为它将大大改善所产生的代码的想法,是没有意义的。
 

1