TDD 中测什么与测试金字塔

把 TDD 的测试边界、行为导向原则和测试金字塔放在一起理解,避免写出慢而脆的测试套件。

#type / synthesis #status / growing #tech / dev / test

[!info] related notes

TDD 中测什么与测试金字塔

范围

这篇笔记不讲 TDD 的流程,而是回答两个更容易写歪的问题:

  • TDD 最值得测什么
  • 测试套件应该如何分层,才不会又慢又脆

为什么要放在一起理解

如果只知道 Red-Green-Refactor,但不知道该把测试火力集中在哪里,最后很容易得到两种坏结果:

  • 测了一堆贴实现细节的测试,一重构就碎
  • 把太多验证挤到慢测试里,反馈速度越来越差

所以“测试边界”和“测试分层”最好一起看。

TDD 更适合测什么

  • 业务规则、定价、计费、权限、状态流转
  • 解析器、转换器、校验器
  • 纯函数和领域服务
  • 历史 bug 的回归保护
  • 和外部系统交界处的契约行为

这些地方的共同点是:行为相对稳定,失败信号清楚,而且值不值得长期保护很明确。

不要优先测什么

  • UI 探索期里还在快速变化的细节
  • 一次性脚本和临时验证代码
  • getter / setter 这类 trivial code
  • 过度依赖内部调用顺序的测试

这些内容要么变化太快,要么价值太低,要么会让测试和实现耦合得过紧。

一个核心原则:测行为,不要测实现

更稳的测试是盯住外部可观察行为:

  • 输入是什么
  • 输出是什么
  • 状态如何变化
  • 用户或调用方能观察到什么

而不是盯住:

  • 私有方法怎么拆
  • 内部调用了几次
  • 临时变量长什么样

测试越贴近实现细节,重构成本就越高。

另一个核心原则:优先测 public interface

当你非常想直接测 private method 时,通常先该问的不是“测试怎么写”,而是“这个类是不是已经太大了”。

很多时候,把职责拆出来,再从公共接口测,设计和测试都会更干净。

为什么要强调测试金字塔

健康的测试结构通常是:

  • 大量小而快的单元测试
  • 少量集成测试
  • 极少量端到端测试

这样做的目的不是追求教条,而是让反馈速度尽量保持在低成本区间。

如果把大部分验证都推给 E2E,就容易得到一个慢而脆的“冰淇淋筒”测试套件。

Google 的另两把尺子:size 和 scope

只看“单测 / 集成 / E2E”还不够,Google 还强调两把补充尺子:

  • size:测试允许占用多少资源、跑在什么约束下
  • scope:测试打算验证多少代码

这两个维度提醒我们:

  • 小测试通常更快、更确定、更适合高频运行
  • 大测试 fidelity 更高,但成本、脆弱性和等待时间也更高

所以测试组合不是层级鄙视链,而是多维权衡。

为什么 E2E 只能保留 bare minimum

E2E 最接近真实用户路径,但也最慢、最贵、最容易 flaky。

更合理的用法是:

  • 只盯 Critical User Journeys
  • 把大部分规则验证压回更低层
  • 把高层测试数量压到足够少

这样才能既保留真实感,又不把反馈速度拖死。

一个实用判断法

为某个行为补测试时,可以先问三件事:

  1. 这个行为的价值是否足够高,值得长期保护
  2. 我能不能在更低层、更快的位置验证它
  3. 这个断言是在验证外部行为,还是在偷看实现细节

如果这三问都过了,测试通常更稳。

最短记忆方式

TDD 不是“到处加测试”,而是把最值得保护的行为,尽量放到最快、最抗重构的测试层里。

参考资料

创建于 2026/4/23 更新于 2026/5/27