Files
2026-07-13 21:36:19 +08:00

2.6 KiB
Raw Permalink Blame History


Deepening

如何安全地深化一组浅层模块(给定其依赖关系)。假设您已熟悉 LANGUAGE.md 中的词汇——module(模块)interface(接口)seam(接缝)adapter(适配器)

依赖分类

在评估一个可供深化的候选模块时,请对其依赖进行分类。分类决定了深化后的模块如何在其接缝处进行测试。

1. 进程内(In-process

纯计算、内存状态、无 I/O。始终可深化——合并模块并通过新接口直接测试。无需适配器。

2. 本地可替代(Local-substitutable

具有本地测试替身的依赖(PGLite 替代 Postgres、内存文件系统)。如果替身存在则可深化。深化后的模块使用测试套件中运行的替身进行测试。接缝是内部的;模块外部接口上没有端口。

3. 远程但自有(端口与适配器,Ports & Adapters

你自己跨越网络边界的服务(微服务、内部 API)。在接缝处定义一个端口(port(接口)。深层模块拥有逻辑;传输层作为**适配器(adapter)**注入。测试使用内存适配器。生产环境使用 HTTP/gRPC/队列适配器。

建议表述:"在接缝处定义一个端口,为生产环境实现一个 HTTP 适配器,为测试环境实现一个内存适配器,这样即使跨网络部署,逻辑也位于一个深层模块之中。"

4. 真正的外部(模拟,Mock

你不控制的第三方服务(Stripe、Twilio 等)。深化后的模块将外部依赖作为注入的端口接收;测试提供模拟适配器。

接缝规范

  • 一个适配器意味着一个假设性的接缝。两个适配器才意味着一个真实的接缝。 除非至少有两个适配器有合理理由(通常是生产环境 + 测试环境),否则不要引入端口。单个适配器的接缝只是间接层。
  • 内部接缝与外部接缝。 一个深层模块可以有内部接缝(对其实现私有,供自身测试使用),也可以有其接口处的外部接缝。不要仅仅因为测试使用了内部接缝,就将其暴露到接口上。

测试策略:替换,而非叠加

  • 一旦深化模块接口处的测试存在,浅层模块上的旧单元测试就变成了废物——删除它们。
  • 在深化模块的接口处编写新测试。接口就是测试面。
  • 测试应通过接口断言可观察到的结果,而非内部状态。
  • 测试应能经受内部重构——它们描述的是行为,而非实现。如果实现发生变化时测试也必须随之变化,说明它测试到了接口之外。