AI 帮你给失败测试找原因:三类根因分类与安全兜底设计

如果你在团队里写过自动化测试,大概率遇到过让人头疼的 Flaky Test(不稳定测试):代码一行没改,同样的用例跑出来时好时坏。这篇想跟你聊清楚两件事:一是面对失败如何用 AI 做根因分类,分清真 bug、环境问题还是用例本身设计有缺陷;二是当 AI 分类不准时,工程上该怎么兜底,而不是盲目相信模型。
先明白 Flaky Test 的三类根因
处理不稳定测试,第一步不是找工具,而是先学会分类。一次失败背后通常只有三种可能,你对号入座即可:
- 真回归 Bug:代码改坏了,缺陷真实存在,通常能稳定复现。
- 环境问题:网络抖动、时序竞争、并发资源冲突等外部干扰,代码本身没问题。
- 测试自身设计缺陷:用例不够健壮,比如依赖了执行顺序、用固定 sleep 等待异步操作。
分类之所以重要,是因为不同根因对应的修法完全不同:真 bug 要改业务代码,环境问题要修基础设施,测试缺陷则要重写用例。如果分不清,技术债就会越积越多——到最后没人知道哪些测试是真的不稳定,更不知道该什么时候修。
传统做法:用统计「掩盖」不稳定性
行业内最主流的两个插件是 pytest-rerunfailures(477 star,pytest-dev 官方维护)和 Box 公司的 flaky(397 star),机制出奇地简单:给测试打个标记(比如 @pytest.mark.flaky(reruns=3)),失败就自动重跑,只要有一次通过就算过。
这套机制完全不做任何根因分析。它的假设很粗暴:真 bug 会稳定复现,重跑几次还失败大概率是真的;而偶发不稳定重跑一次就好了。它便宜、零配置,能解决 CI 流水线「要不要变绿」的燃眉之急,但回答不了「为什么会失败」这个长期问题。
这种思路有两个明显天花板:一是只在高频时序下触发的竞态条件 bug,重跑通过后会被当成 flaky 直接放过;二是设计有缺陷的测试,只会不断「凑巧」通过,你永远等不到它提示你该重写用例了。另一个叫 DeFlaker(39 star,乔治梅森大学实验室出品)的 Maven 扩展聪明一些,它把 git diff 改动行和测试代码覆盖率做交叉比对:失败用例执行过改动代码,就判定为疑似真回归;没执行过,就标记为跟改动无关。但纯确定性手段只能回答「跟这次改动有没有关系」,细分到环境问题还是测试设计问题时,依然无能为力。
用 LLM 做根因分类:开放式场景才有价值
前面提到的几个传统工具,本质上都只看「重跑之后是否失败」这一个信号,根本不读失败的具体内容。LLM 的真正价值恰好在此时体现出来:它不怕麻烦地替你阅读报错内容,给出有依据的判断。
目前能看到的、真正用 LLM 做根因分类的开源实现是 pytest-triage(2 star,2026 年 7 月创建)。它的做法很值得借鉴:读取每个失败测试的 traceback、异常类型和 stdout/stderr 尾部日志,调用 Anthropic 或 OpenAI 的 API,通过 tool use 强制模型输出结构化结果,而不是自由发挥。
它的输出结构就是照着三类根因设计的,我们日常自己做 AI 分析时也可以直接套用:
| 字段 | 含义 | 示例 |
|---|---|---|
| category | 分类:regression / flaky / env / test_bug / unknown | env(环境问题) |
| confidence | 判断的置信度 | 0.85 |
| hypothesis | 具体的假设陈述 | ConnectionError(tests/test_shop.py) |
| suggested_fix | 建议的修复方式 | 检查数据库连接超时配置 |
注意里面有一个非常务实的 unknown 分类——模型判断不了时,就诚实地说不知道,绝不硬猜。它的诊断产出也是「一句人能读懂的假设 + 一个可执行的建议」,帮你省掉自己去翻日志、猜原因的时间。但要注意:这份价值完全建立在「分类准确」的前提上,AI 一旦分错,省下的排查成本可能反而变成新增的误导成本。
真正重要的:给 AI 划定权力边界
既然判断可能出错,系统的默认姿态应该是什么?pytest-triage 给出了我认为最能抄作业的回答:它不去追求「让 AI 更准」,而是把 AI 判断可能出错的代价限制在最小范围内。四条安全约束都值得照搬:
- AI 绝不影响测试的真实结论:AI 只负责在失败之后追加一段诊断说明,绝不允许反过来决定这次运行算通过还是失败。即便模型报错或返回垃圾内容,整个测试结果和没装插件时逐字节一致。
- 默认关闭:装上插件不改变现有行为,必须显式开启才会调用 LLM,防止意外花钱。
- 预算硬上限:默认每次运行最多调用 10 次模型。即使有 300 个失败用例,第 11 个开始直接返回 unknown,绝不多花一分钱(设为 0 则纯本地报告模式)。
- 四层防御包装:缓存(命中不重复计费)包着断路器(连续报错两次直接跳闸,后续一律 unknown)包着预算(耗尽即拒绝)包着超时(设硬性时长,独立线程运行,最差也只是丢弃诊断,绝不拖垮正常流程)。
合在一起看,它的核心原则是把「相不相信 AI」这个态度性问题,转化为纯工程问题:即使 AI 判断的准确率是 0,最坏情况也只是报告里出现一堆 unknown,测试的通过/失败结果、运行耗时和成本完全不受影响。这与成熟项目用 AI 的纪律如出一辙:尽量把模型圈定在确定性方法覆盖不了的最窄环节里。
小结
面对不稳定测试,传统重跑插件解决的是「现在让 CI 变绿」的短期焦虑,低成本但回避根因;diff 加覆盖率交叉验证能找出与改动相关的回归,但分不细。给失败日志接 LLM 做根因分类是目前更贴近「排查」本质的做法。学会用像「regression/flaky/env/test_bug」这样的结构化输出去分析失败日志,同时提前设计好降级策略——把模型的权力牢牢关进笼子里,让 AI 只做诊断顾问,不裁判结果,这才是普通团队把 AI 用在测试领域的正确姿势。
本文基于 rss-juejin《LLM 驱动的自动化测试系列(13):Flaky Test 不是重跑几次就能解决的分类问题》深度重写,事实与观点归原作者所有。建议阅读原文。原作者:冬奇Lab。发布于 2026-10-15。