如何知道测试是正确的?

这一反对意见有其合理性,因此我们需要深入理解其背后的逻辑。这种反对意见通常来自那些不熟悉编写自动化测试的人,因为他们误解了我们如何避免编写错误的测试。通过帮助他们了解我们采取的保护措施,我们可以帮助他们重新构建他们的思维方式。

理解编写错误测试背后的顾虑

你会听到的一种反对意见是:“如果测试本身没有测试,我们怎么知道测试是正确的?”当我第一次向团队引入单元测试时,就有人提出了这种反对意见。这引起了分歧。团队中的一些人立刻理解了其价值,另一些人则漠不关心,但还有一些人则表现出明显的敌意。他们认为这种新做法暗示了他们存在某种不足,并将其视为一种威胁。

在这种背景下,一位开发者指出了我解释逻辑中的一个缺陷。我告诉团队,我们不能仅仅依赖对生产代码的视觉检查。是的,我们都擅长阅读代码,但作为人类,我们难免会遗漏一些东西。单元测试可以帮助我们避免遗漏。一位聪明的开发者提出了一个很好的问题:如果视觉检查对生产代码无效,为什么我们认为它对测试代码有效?两者之间有什么区别?

对此,一个恰当的例子发生在我需要测试一些 XML 输出时(我记得那是在 2005 年)。我编写的用于检查 XML 输出的代码确实非常复杂。批评是正确的,我无法通过视觉检查诚实地断言那段测试代码没有缺陷。于是,我应用了 TDD 来解决这个问题。我使用 TDD 编写了一个工具类,可以比较两个 XML 字符串并报告它们是否相同,或者指出第一个差异是什么。它还可以配置为忽略 XML 元素的顺序。我将这段复杂的代码从原始测试中提取出来,并用对这个新工具类的调用替换了它。我知道这个工具类没有任何缺陷,因为它通过了我为其编写的所有 TDD 测试。这些测试覆盖了所有我关心的正常路径和边缘情况。原本被批评的测试现在变得非常简短和直接。

我请提出这一问题的同事审查代码。他们同意,在这种新的、更简单的形式下,他们很乐意从视觉上认可测试是正确的。他们补充了一个前提:“如果工具类工作正常的话。” 当然,我们有信心它通过了我们为其编写的所有 TDD 测试。我们确信它完成了我们特别希望它做的所有事情,因为这些功能都通过了测试的验证。

提供确保我们测试测试的安心

这一论点的核心在于,简短、简单的代码可以通过视觉检查来验证。为了确保这一点,我们保持大多数单元测试足够简单和简短,以便于理解。当测试变得过于复杂时,我们将这种复杂性提取到一个独立的代码单元中。我们使用 TDD 来开发这个单元,并最终使原始测试代码变得足够简单以便检查,同时使测试工具也足够简单以便其测试能够检查,这是一个典型的 “分而治之” 的例子。

在实践中,我们邀请同事指出他们认为测试代码过于复杂而难以信任的地方。我们通过使用简单的工具类来重构它,这些工具类本身也是通过简单的 TDD 编写的。这种方法帮助我们建立信任,尊重同事的合理关切,并展示了我们如何找到方法将所有 TDD 测试简化为可审查的简单代码块。

既然我们已经解决了如何确保测试正确的问题,另一个常见的反对意见涉及对 TDD 的过度自信:即简单地遵循 TDD 过程就能保证代码质量。这真的可能吗?让我们来探讨一下这些论点。