TDD 中测什么与测试金字塔
把 TDD 的测试边界、行为导向原则和测试金字塔放在一起理解,避免写出慢而脆的测试套件。
#type / synthesis
#status / growing
#tech / dev / test
[!info] related notes
- 所属 MOC: 测试驱动开发 MOC, Testing MOC
- 相关概念: 测试驱动开发(TDD), 自测试代码(Self-Testing Code), 前端测试, 后端测试, 测试数据库的方案
- 易混淆概念: 压力测试
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
- 把大部分规则验证压回更低层
- 把高层测试数量压到足够少
这样才能既保留真实感,又不把反馈速度拖死。
一个实用判断法
为某个行为补测试时,可以先问三件事:
- 这个行为的价值是否足够高,值得长期保护
- 我能不能在更低层、更快的位置验证它
- 这个断言是在验证外部行为,还是在偷看实现细节
如果这三问都过了,测试通常更稳。
最短记忆方式
TDD 不是“到处加测试”,而是把最值得保护的行为,尽量放到最快、最抗重构的测试层里。