我们总是可以稍后再测试,对吧?
一种替代先编写测试的方法是先编写代码,然后再编写测试。这一部分将比较和对比在编写代码之后编写测试与在编写代码之前编写测试。
一种编写测试的方法涉及编写大块代码,然后为这些代码添加测试。这是一种在商业编程中使用的方法,其工作流程可以如下所示:

在选择要开发的用户故事后,编写一个或多个生产代码片段。然后编写测试!
学术研究似乎对测试后与测试前是否有差异持不同意见。根据 ACM 2014 年的一项研究,结论的摘录如下:
“……静态代码分析结果在统计学上显著支持 TDD。此外,调查结果显示,实验中大多数开发者更喜欢 TLD(测试后开发)而不是 TDD(测试驱动开发),因为所需的学习曲线较低。”
然而,一位评论者指出,在这项研究中,以下情况适用:
“……仅从31名开发者中的13名获得了可用数据。这意味着统计分析是使用七人(TDD)和六人(TLD)组进行的。实验被发现缺乏统计效力且结果不确定,这并不令人意外。”
其他研究论文似乎也显示出类似的不尽如人意的结果。那么实际上,我们应该从中得出什么结论呢?让我们考虑一下测试后开发的一些实际细节。
后测试对于 TDD 初学者更容易
研究的一个发现是,TDD 的初学者发现测试后开发更容易上手。这似乎是合理的。在我们尝试 TDD 之前,我们可能认为编码和测试是不同的活动。我们根据一些启发式方法编写代码,然后我们弄清楚如何测试该代码。采用测试后方法意味着编码阶段基本上不受测试需求的影响。我们可以像往常一样继续编码。不必考虑测试对代码设计的影响。这种看似优势是短暂的,因为我们发现需要为测试添加访问点,但我们至少可以轻松上手。
如果我们保持与生产代码同步编写测试,稍后添加测试效果相当好:编写一点代码,然后为该代码编写一些测试——但没有为每个代码路径编写测试仍然是一个风险。
后测试使得测试每个代码路径变得更加困难
反对使用测试后方法的一个合理论点是,它变得更难跟踪我们需要的所有测试。从表面上看,这种说法不能完全正确。我们总能找到某种方法来跟踪我们需要的测试。测试就是测试,无论何时编写。
问题在于添加测试之间的时间增加。我们正在添加更多代码,这意味着在整个代码中添加更多执行路径。例如,我们编写的每个 if
语句代表两条执行路径。理想情况下,我们代码中的每条执行路径都应该有一个测试。我们添加的每条未经测试的执行路径都会使我们低于这个理想数字。这在流程图中直接体现:

这个流程图描绘了一个具有嵌套决策点的过程——菱形——这导致了三个可能的执行路径,标记为 A、B 和 C。执行路径数量的技术度量称为圈复杂度。复杂度分数是根据代码中存在多少线性独立执行路径计算得出的数字。流程图中的代码的圈复杂度为三。
随着我们增加代码的圈复杂度,我们增加了认知负荷,需要记住所有那些我们稍后需要编写的测试。在某些时候,我们甚至可能会发现自己定期停止编码并写下稍后要添加的测试的笔记。这听起来比我们在编写代码时简单地编写测试更为繁琐。
使用测试优先开发时,可以避免跟踪我们尚未编写的测试的问题。