通过六边形架构定义测试边界
这一部分的主题是使用六边形架构如何影响 TDD。知道我们正在使用六边形架构为测试金字塔中的不同测试提供了有用的边界。
从某种意义上说,我们如何组织代码库并不影响我们对 TDD 的使用。代码的内部结构只是一个实现细节,是使我们的测试通过的众多可能性之一。话虽如此,有些代码结构比其他结构更容易使用。使用六边形架构作为基础结构确实为 TDD 提供了一些优势。原因在于端口和适配器的使用。
我们从之前的章节中了解到,为代码编写测试更容易,因为我们可以控制代码运行的环境。我们已经看到测试金字塔如何为我们编写的不同类型的测试提供结构。使用端口和适配器方法为代码中的每种测试提供了清晰的边界。更好的是,它为我们提供了将更多测试带到单元测试级别的机会。
让我们回顾一下哪些类型的测试最适合使用六边形架构编写的每一层软件。
内部测试适用于领域模型
经典的 TDD 使用自内而外的开发方法,我们选择一个特定的软件组件进行测试驱动。这个组件可能是一个单一的函数、一个单一的类,或者一个相互协作的小类群。我们使用 TDD 来测试这个组件作为一个整体,考虑到它向其消费者提供的行为。
这种组件位于领域模型——内部六边形中:

关键优势是这些组件易于编写测试,并且这些测试运行得非常快。所有内容都存在于计算机内存中,没有外部系统需要处理。
另一个优势是复杂的行为可以在这里以非常细的粒度进行单元测试。一个例子是测试用于控制工作流的有限状态机中的所有状态转换。
一个缺点是,如果发生较大的重构,这些细粒度的领域逻辑测试可能会丢失。如果在重构期间删除了细粒度测试下的组件,其相应的测试将丢失——但行为仍然会存在于其他地方,作为重构的结果。重构工具无法做到的一件事是弄清楚哪些测试代码与被重构的生产代码相关,并自动重构测试代码以适应新结构。
外部测试适用于适配器
Mockist 风格的 TDD 从外而内的角度进行开发。这与我们在六边形架构中的适配器层非常匹配。我们可以假设核心应用程序逻辑位于领域模型中,并且已经在那里用快速的单元测试进行了测试。这使得外六边形中的适配器需要通过集成测试进行测试。
这些集成测试只需要覆盖适配器提供的行为。这应该非常有限。适配器代码仅将从外部系统使用的格式映射到领域模型所需的内容。它没有其他功能。
这种结构自然遵循测试金字塔的指导原则。需要较少的集成测试。每个集成测试只需要测试一小部分行为:

这种测试风格独立验证适配器。它将需要一些端到端的 “快乐路径” 测试来显示整个系统使用了正确的适配器。
用户故事可以跨领域模型进行测试
拥有包含所有应用程序逻辑的领域模型的一个好处是,我们可以测试完整用户故事的逻辑。我们可以用测试替身替换适配器,以模拟外部系统的典型响应。然后我们可以使用 FIRST 单元测试来执行完整的用户故事:

优势在于 FIRST 单元测试的速度和可重复性。在其他代码结构方法中,我们可能只能在测试环境中将用户故事作为端到端测试来执行,并伴随所有相关的缺点。能够在单元级别测试用户故事逻辑——跨越整个领域模型——使我们高度相信我们的应用程序将满足用户的需求。
为了确保这种信心,我们需要适配器层的集成测试,以及一些跨选定用户故事的端到端测试,确认应用程序作为一个整体正确连接和配置。这些更高级别的测试不需要像围绕领域模型执行的用户故事测试那样详细。
在领域模型周围拥有一组良好的用户故事测试还支持领域模型内的大规模重构。我们可以有信心在这些广泛范围的用户故事测试的指导下重构内部六边形。
这一部分向我们展示了如何将测试金字塔中的不同类型测试与六边形架构中的不同层次联系起来。