本节摘要:测试数量上去之后,质量不合格的测试比没有测试更糟——它们随机变红、互相污染、拖慢循环。本节把好测试的标准收敛为四根柱子:快、独立、可复现、自解释,给出每根柱子的检查动作与对治手法。读完你应能给任何一条测试做体检并开出处方。
早期给测试立规矩的人,动机都很朴素:自动化测试刚兴起的年代,团队最深的恐惧不是"没有测试",而是"测试随机失败"——红色出现时你不知道是实现坏了还是测试抽风,几次误报之后没人再信测试,整套资产作废。xUnit 一系的实践者们于是反复提炼"什么样的测试值得信任",从单元测试之父们的口诀到 TODAY、FIRST 等各版缩写,说的其实是同一件事:测试必须像好的测量仪器——量得快、互不干扰、读数稳定、刻度清楚。本节把它收敛成四根柱子。

四根柱子不是并列的装饰,而是循环的地基:快撑着十分钟节拍,独立撑着任意重构的安全网,可复现撑着"红灯即故障"的信任,自解释撑着测试当文档的副业。任何一根塌了,前面辛苦建立的节律都会连锁垮掉。
实战里柱子偶尔冲突:为了独立,每个用例重建整套数据,速度就掉了;为了可复现,把网络封进替身,真伪性又打折扣。冲突时的优先级建议是:可复现优先于一切——读数不稳的测试没有信用,信用崩了其他柱子全部白搭;独立次之,它决定测试集能不能并行、能不能红得指名道姓;快排第三,它有预算线(条均百毫秒)但允许个别例外挂慢标记;自解释贯穿全程,与其他柱子几乎不打架。排好优先级,多数"要不要用大夹具"式的争论都有了解答:在守住前两根的前提下尽量快,快不起来的部分隔离出去(5.1 的分层),绝不牺牲前两根换取整体提速。
对照着看一条灯塔小组真实出现过的测试,四根柱子挨个体检:
# 病例:四柱全塌 user = create_user(" tester ") # 模块级共享状态,还带着没清理的空格 def test_discount(): set_global_promo("SUMMER") # 依赖全局促销位,别的用例会动它 time.sleep(1) # 等缓存过期,硬塞等待 total = checkout(CART_WITH_3_ITEMS) # 名字看不出在验证什么 assert total == 270 # 期望值没有推导过程,魔法数
会诊记录:不独立——全局促销位是公共寄存器,谁后写谁说了算;不可复现——sleep 猜时间,抢不到就红;慢——每跑一次白等一秒;不自解释——270 从何而来要翻需求文档。同一场景重写成四柱俱全的版本:
def test_member_discount_applies_10_percent_off_300_order(): cart = cart_with(items=[("SKU", Decimal("300.00"))]) pricing = Pricing(promo=fixed_promo("SUMMER", percent=Decimal("0.10")), clock=FakeClock(at="2026-06-01T10:00:00")) assert pricing.member_total(cart) == Decimal("270.00")
注意改动的方式:不是"重写",而是把四根柱子逐一装回去——前提自己构造(独立)、时钟显式注入假对象(可复现)、没有等待(快)、名字与断言里保留推导链(自解释)。后面两节讲的 fixture 与替身,就是把这两种"装柱子"的动作变成批量生产工具。
排障时反着用最顺手:先看症状,再查塌了的柱子。
| 症状 | 塌了的柱子 | 第一步处置 |
|---|---|---|
| 单跑绿、全批红 | 独立 | 找模块级可变状态,改 fixture 重建 |
| 偶尔红、重跑绿 | 可复现 | 排查时间、随机、网络、字典顺序 |
| 跑批要等半分钟 | 快 | 慢依赖换替身,I/O 型用例隔离 |
| 红了要开会解释 | 自解释 | 改名、断言带上下文消息 |
| 改一处实现红十条 | 自解释(锁细节) | 断言从结构词换成行为词 |
| 新人不敢动测试 | 全体(看不懂的都不敢碰) | 先补名字与前提注释,再谈重构 |
⚠️ 最被低估的是"自解释"。它不影响测试能不能跑,只影响三个月后你能不能看懂——而测试的半衰期恰恰长过实现:实现会重写,测试断言的行为契约往往一直活着。
💡 给"快"立个可执行的预算:单条用例超过一百毫秒就该出示理由,整批超过十秒就该分层——慢的那部分标记出来放到流水线晚段跑(第 6 章),主循环里只留毫秒级用例。
预算贴在显眼处比记在脑子里管用:pytest 配置里加上耗时排行参数(--durations 在 CI 输出的摘要里常驻),超预算的用例在每次流水线日志里自动点名。可视化不改变任何行为,却让"慢"从感觉变成事实——事实才配得上治理动作。
体检不必等测试腐烂再做。灯塔小组后来立了个小规矩:review 测试代码时用四柱当清单,提问固定成四句——它快吗、单跑绿吗、重跑绿吗、红了能看懂吗。四问全过才允许合入。日常写测试时则反过来:先写名字(需求句),再写断言(行为),最后补前提(构造)——顺序对齐了,自解释与独立通常自动到位,剩下快与可复现靠 fixture 和替身这两件工具,正是接下来两节的内容。
四柱还有一层组织价值:它们给了团队讨论测试质量时的共同词汇。以前争论"这条测试写得不好"容易沦为口味之争,现在可以精确到"它塌在可复现上,因为读了系统时钟"——问题一旦被准确命名,解法往往是现成的。共享词汇看似小事,却是第 7 章团队能否落地节律的隐形基础设施。