flaky test:为什么是大害,怎么发现
每个团队都有那么几条”神经刀”用例:不改代码,上午红下午绿,再跑一遍又好了。大多数人把它当小麻烦——烦归烦,重跑一下就行。这篇文章要讲的是相反的判断:flaky test 是测试体系的头号大害,以及怎么把它从套件里捞出来。
一句话版本:
flake 烧的不是机时,是红灯的可信度——而测试体系的全部价值,就建立在红灯可信上。
一、为什么是大害
1. 它制造”狼来了”文化
测试体系的价值 = 红灯出现时,团队的第一反应是”查产品”。flake 把这个反应腐蚀成”再跑一遍试试”。一旦 rerun 成为文化,真 bug 的红灯也会被 rerun 盖掉——最危险的不是 flake 本身,是它给真回归提供了隐身衣。
2. 它比没有用例更糟
没有用例,团队知道自己在裸奔,会保持警惕;flake 的用例让团队以为自己有测试,实际没有——还每天消耗注意力去鉴别”这次是真红还是假红”。
3. 成本结构全面恶化
- 直接成本:rerun 的机时(HIL 上放大 10~100 倍)、CI 排队;
- 间接成本:鉴别成本远超执行成本——每个失败都要人查”产品错了还是测试抽风”;
- 体系成本:门禁被 flake 反复卡住 → 工程师开始绕过门禁 → 门禁一旦被绕过,整套体系崩溃。
4. 它有传染性
一个 flake 不处理,等于向全组宣布”失败是可以 rerun 掉的” → 写用例的标准降低 → 更多 flake 涌入。flake 率只涨不跌是默认趋势,必须主动治理。
5. 汽车行业的两个放大器
- HIL 机时贵:一次 rerun 烧的是全组排队资源;
- ISO 26262 证据链:审核时”这条用例 3 次过 2 次”是要解释的工作产品——flake 污染追溯链(执行记录是合规证据,不只是工程数据)。
二、根因分类:检测的前提
flake 不是随机鬼影,每个 flake 都有根因。知道根因类型,才知道用什么钓法:
| 根因 | 典型表现 | 对应钓法 |
|---|---|---|
| 时序假设 | sleep(100) 等结果,机器慢就挂;断言假设零时延 |
扰动下跑(CPU 打满 / 总线拉载) |
| 环境泄漏 | 用例间共享状态没清理,依赖执行顺序 | shuffle 跑(--gtest_shuffle) |
| 随机性没控住 | 没固定种子、依赖时钟/日期/网络 | 同一 commit 重复跑 |
| 资源边界 | 超时阈值贴着正常耗时设;泄漏导致第 N 条才挂 | 长程重复 + 资源监控 |
| 产品真 bug | 并发竞态、状态机死角 | ⚠️ 见下面警告 |
重要警告:flake 有时是产品真 bug 的早期信号——并发竞态、状态机死角,在测试里就表现为”时过时不过”。一律怪测试环境,等于把产品的早期警报当噪音扔了。治理流程里必须有”排除产品嫌疑”这一步。
三、五种检测手段(按威力排序)
- 统计法(黄金标准):同一 commit 重复跑 N 次(夜间跑 10 遍),结果不一致的就是 flake。排除了代码变量,剩下的不一致只能来自测试或环境;
- 历史数据分析:看每条用例的红绿翻转次数(flips)——真回归的失败曲线是阶跃(从某个 commit 起 100% 红,修好后转绿);flake 的失败曲线是散点(红绿随机交替,与 commit 无关)。按 flips 排序取 top N,就是 flake 候选清单;
- rerun 即过 = 强信号:CI 里失败后原地 rerun 就过的,自动打标(
retried-pass)。很多 CI 平台原生支持 retry 并记录——这个数据不收集就白流走了; - 新用例准入检测:新用例入库前重复跑 50~100 次(不同负载、不同种子),把 flake 检测前置到用例评审——比入库之后再捞便宜得多;
- 扰动/混沌法:CPU 打满、网络注延迟、HIL 总线负载拉满时跑——时序假设型 flake 在扰动下批量现形。
四、治理三招
- 隔离区(quarantine):发现即移出门禁,打
quarantine标签,限期修,修不好删。留在门禁里的 flake 每天都在烧信任,比删掉损失大; - 修的方向对着根因:
sleep换成条件等待/事件驱动;固定随机种子;teardown 清理共享状态;超时阈值按 p99×3 设,而不是贴着均值设; - flake 率上报:作为测试体系健康度 KPI 跟踪趋势——只许降,不许涨。
回到开头那句话:flake 烧的是红灯的可信度。治理 flake 的目标不是”套件更绿”,而是让红灯重新等于”查产品”——做到那一天,测试体系才算真正在工作。
附:名词解释(按出场顺序)
| 名词 | 通俗解释 |
|---|---|
| flaky test / flake(不稳定用例) | 不改代码、时而过时不过的用例,测试结果像掷硬币 |
| rerun(重跑) | 失败后不改代码不换环境,原地再跑一遍 |
| HIL | 硬件在环测试:真实控制器接仿真台架,机时昂贵 |
| CI | 持续集成:每次提交自动构建并跑测试的流水线 |
| 门禁(质量门禁) | CI 流水线上的自动关卡:不达标就不许合入 |
| ISO 26262 | 汽车功能安全标准,执行记录属于审核要查的合规证据 |
| 证据链 / 追溯链 | 让测试结论可被第三方复算、可追溯到需求的一整套记录 |
| flips(红绿翻转次数) | 同一条用例在相邻执行间结果翻转的次数;flake 检测的排序指标 |
| retried-pass | 失败后重跑即过的用例标记,是 flake 的强信号 |
| quarantine(隔离区) | 把 flake 移出门禁单独看管、限期修复的标签机制 |
| p99 | 第 99 百分位耗时:100 次执行从快到慢排序,第 99 名的值,设超时阈值的基准 |
| KPI | 关键绩效指标;flake 率是测试体系健康度的 KPI 之一 |
下一步:
查看全部文章