单元测试 – FIRST 测试

在本节中,我们将探讨测试金字塔的底层,即由单元测试组成的部分。我们将分析为什么这一层对成功至关重要。

到目前为止,我们已经非常熟悉 FIRST 单元测试了。前面的章节已经详细介绍了这些内容。它们是单元测试的黄金标准。它们运行速度快,可重复且可靠。它们彼此隔离运行,因此我们可以运行一个,或者运行多个,并以我们选择的任何顺序运行它们。FIRST 测试是 TDD 的核心力量,使我们能够在编码时使用快速反馈循环。理想情况下,我们所有的代码都将落入这个反馈循环中。它提供了一种快速、高效的工作方式。在每一步中,我们都可以执行代码,并证明它正在按我们的意图工作。作为一个有益的副产品,通过编写测试来覆盖代码中每个可能的有用行为,我们最终将覆盖每个可能的代码路径。当我们以这种方式工作时,我们将获得 100% 有意义的单元测试代码覆盖率。

由于其优势,单元测试构成了我们测试策略的基石。它们被表示为测试金字塔的基础。

单元测试有其优势和局限性,如下表总结:

Table 1. Table 10.1 – Unit test advantages and disadvantages
优点 限制

这些是运行最快的测试,并为我们的代码提供了最快的反馈循环。

这些测试的范围较小,这意味着所有单元测试通过并不能保证整个系统正常工作。

稳定且可重复,且不依赖于我们无法控制的外部因素。

它们可能会与实现细节紧密绑定,导致未来的扩展和重构变得困难。

能够提供对特定逻辑集的详细覆盖,准确定位缺陷。

不适合测试与外部系统的交互。

在任何系统中,我们预计在单元测试级别拥有最多的测试数量。测试金字塔以图形方式表示了这一点。

在现实世界中,仅使用单元测试无法实现全面覆盖,但我们可以改善我们的情况。通过将六边形架构应用于我们的应用程序,我们可以将大部分代码纳入单元测试的覆盖范围。我们快速运行的单元测试可以覆盖很多领域,并为我们的应用程序逻辑提供很大的信心。我们可以确信,如果外部系统按我们预期的方式运行,我们的领域层代码将能够正确处理我们考虑到的每个用例。

仅使用单元测试时的测试位置如下图所示:

image 2025 01 12 17 17 34 313
Figure 1. Figure 10.3 – Unit tests cover the domain model

单元测试仅测试领域模型的组件。它们不测试外部系统,也不使用外部系统。它们依赖于测试替身(test doubles)来模拟我们的外部系统。这为我们在开发周期速度上提供了优势,但缺点是这些与外部系统的连接仍未经过测试。如果我们有一段经过单元测试的代码访问仓库接口,我们知道它的逻辑可以与存根仓库一起工作。它的内部逻辑甚至会有 100% 的测试覆盖率,并且这是有效的。但我们还不知道它是否能够与真实的仓库一起工作。

适配器层代码负责这些连接,而它在单元测试级别并未被测试。为了测试这一层,我们需要一种不同的测试方法。我们需要测试当我们的领域层代码与实际外部系统集成时会发生什么。

下一节将探讨如何使用一种称为 集成测试 的测试方法来测试这些外部系统适配器。