本节摘要:写了测试,为什么 bug 还是溜进了线上?多数时候答案不是"测试太少",而是测试集里混进了反模式:看起来在测、实际在演的假测试。本节收录七种高发反模式,各配识别信号、真实病例与处方。读完你应能给测试集做一次全面体检,并知道自己过去踩过哪几口锅。
七种反模式按"出现频率 × 危害"排序,先总览再逐个会诊。读表的方法:症状列给"怎么认出它",处方列给"去哪找解药"——本节展开的四种都在测试集内部,其余三种的病灶分散在分层与依赖管理上,按处方页回翻即可。表会随团队进化:每次事故复盘后往里补一行你自己的新反模式,两年后这张表就是团队的独家病例库,比任何通用清单都贴身。
| 反模式 | 一句话画像 | 识别信号 | 处方页 |
|---|---|---|---|
| 假红灯 | 从没验证过会红的测试 | 断言写错也能绿 | 本节 |
| 快乐路径症 | 只测一切顺利的世界 | 零边界值、零异常用例 | 本节 |
| 测试调试 | 离开调试器跑不红的用例 | 依赖执行顺序或断点时机 | 本节 |
| 替身越界 | mock 了协议之外的东西 | 改内部实现红一片 | 见 3.3 |
| 慢用例堆场 | 秒级预算形同虚设 | 全批跑要等数分钟 | 见 3.1、6.2 |
| 跳过泛滥 | 跳过标记满天飞 | skip 数量持续增长 | 本节 |
| 倒金字塔 | 端到端当主力 | 反馈按分钟计 | 见 5.1 |
假红灯的真名该叫"从未红过的绿灯"。它的成因朴素得可怕:写完实现顺手写测试,或者写完测试没验证失败原因就开写实现——断言写反了、测了个空操作,测试照样绿,而且永远绿。它给团队的虚假安全感最强,因为它有名字、有断言、有覆盖率贡献,唯独没有检验能力。
# 病例:断言对象是局部变量,与被测函数毫无关系 def test_discount_should_apply(): result = apply_discount(Decimal("100"), percent=Decimal("0.1")) discounted = Decimal("90") # 手算的正确答案躺在这里 assert discounted == Decimal("90") # 测的是自己给自己出的题
这条测试永远绿——它断言的是两个手写字面量相等,apply_discount 的结果 result 根本没参与。修复后顺便演示标准防线:
def test_discount_applies_ten_percent(): result = apply_discount(Decimal("100"), percent=Decimal("0.1")) assert result == Decimal("90")
防线不是这次修复,而是流程:新测试必须先红一次再写实现——第一章的三定律在反模式面前的价值,到这里才完全显形。对存量测试,抽查法有效:随机挑几条,故意改坏对应实现,看它红不红。不红的,当场除名或修复。
快乐路径症的患者写得出"正常输入得到正常输出",写不出"世界找麻烦"的用例。病根常在需求理解:只听懂了"应该怎样",没追问"不满足时呢"。处方是给每条功能补一组边界审问:空输入、零、负数、极大值、字符集外的字符、并发修改、依赖不可用。审问的产出直接进参数化表格——2.4 的表格行配上"这行为什么存在"的注释,就是边界审问的存档。灯塔小组的固定动作:每个功能的测试里,边界用例数量不得少于快乐路径——比例倒挂即视为审问没做。
测试调试指测试只有在调试器里单步执行才能复现的红绿——十有八九背后藏着时序依赖:依赖别的用例先跑、依赖文件系统残留、依赖网络延迟。它把"测试"退化成"个人仪式",除作者外无人能跑。处方回到三根柱子:独立(不共享现场)、可复现(封死时间随机网络)、快(不靠真实延迟)。与测试调试常伴生的是"本地通过依赖特定顺序"的隐形契约——某条用例恰好污染了现场,恰好后面的用例又依赖这种污染,单看哪条都无辜。诊断动作:用随机顺序插件把全批打乱跑一周(6.1 的 randomly),隐形契约当场现形。
跳过泛滥是团队的慢性病:某个用例在某个环境偶尔红,没人想修,加个跳过标记,世界暂时清净。清净是有代价的——每个跳过标记都是一块"此处有意不验证"的告示牌,半年后测试集的有效面积缩水三成。处方是 6.2 的红灯纪律落地版:跳过必须带理由带期限(标记里写明问题单号),期限内不修复就删除——删除至少让账面诚实。
把"定期体检"落成可执行的动作清单,每月挑半天跑一遍,每次覆盖一个模块:随机抽五条测试,故意改坏对应实现,验它们红不红(抓假红灯);统计边界用例与快乐路径的比例(抓快乐路径症);翻跳过标记清单,核对理由与期限(抓跳过泛滥);看条均耗时排行前十名(抓慢用例堆场);抽三条用 mock 最多的测试,确认替身都在边界内侧(抓替身越界)。清单产出物是一页体检纪要——发现什么、修什么、谁修、何时复核。别小看这半天的例行公事,反模式发酵成灾难平均要几个月,月检正好掐在幼苗期。
反模式之间会互相催化:替身越界造成改实现红一片,逼出跳过泛滥;跳过泛滥抬高漏测率,事故后又引来一波"补测试运动",补出来的多是快乐路径与假红灯。治疗要点在源头而非症状——源头几乎总是同一个:某个环节绕过了三定律。图鉴的正确用法不是抓犯人,而是定期体检:每月挑一个模块做抽查(改坏实现验红、数边界比例、看跳过清单),把反模式掐死在幼苗期。
⚠️ 图鉴最容易被误用成扣分工具。反模式的出现反映的是流程漏洞,不是个人品德——拿图鉴考核团队,得到的只会是更隐蔽的假测试。
💡 判断测试集健康度有个粗但灵的信号:随机抽十条测试,读名字能猜出各自验证什么业务行为的比例。低于八成,测试集已经和实现一起腐烂了,先补名字再谈其他。