自动化真正烧钱的阶段不是"写",是"养"。一个普遍的行业体感是:套件跑起来后,团队七八成的自动化时间花在了失败用例的分析和维护上。CI 一轮跑完掉了二十条,你得逐条判断——产品真出 bug 了?flaky 重跑就过?环境挂了?是这次业务改版让旧脚本不再适配,还是脚本自己就写坏了? 这判断每天重复、耗神,还高度依赖老员工。

这一篇讲 AI 怎么一次接手:把整份失败日志丢给它,一轮完成分诊、聚类,并对大部分 flaky 直接给出根因和修法方向;人只处理真缺陷和少数疑难。

技术栈沿用系列:Python + Pytest + Playwright(建议开 pytest-rerunfailures,重跑结果是判 flaky 的硬信号)。方法论对 Selenium / Cypress / Appium 同样适用。

一、先看清:一次失败,可能是四种完全不同的东西

"用例红了"是结果,不是病因。红灯里通常混着四类,处置天差地别:

类别 典型特征 该往哪走
真缺陷(产品回归) 断言业务值不符、确定性红、和本次改动相关 立即拦、提 bug,这是测试的本职战果
flaky(不稳定) 同一版本重跑结果不一致、时红时绿 挂账,按根因治理(本文重点)
环境 / 数据问题 502、依赖服务挂、造数失败、DB 连不上 恢复环境,不该算到产品头上
脚本不适配 / 坏了 产品改版后流程、文案、定位器变了脚本没跟上;或定位器必现找不到、import 报错、封装改了没同步 修测试代码,不是产品问题

传统模式全靠人逐条点开日志。而这四类在日志里本就有可区分的硬信号,这是能整批交给 AI 的前提。

二、把失败日志和截图喂给 AI,一轮出三件结果

2.1 喂给 AI 的两样东西:失败日志、失败截图

pytest 一轮跑完,失败堆栈、错误类型、rerun 的重跑记录都在文本日志里;再把每条失败的界面截图一起给 AI,两样就够分诊和判断大部分根因——几十上百条失败,现在的上下文一次装得下,不用逐条发。

截图是信息量最大的一手证据(Playwright 配 screenshot: only-on-failure 后失败现场自动落盘,CI 报告点开那条失败就能看到),现在模型本就多模态,直接贴图:是不是跳回了登录页、有没有弹窗/遮罩盖住、元素到底渲染没渲染、是不是落到了错误页面,很多根因看一眼图就定了,比读堆栈快得多,对"选择器 / 登录态 / 动画遮罩"这几类尤其管用。

日志和截图之外,有一样东西能顺手给就更好:本次改动的文件列表(git diff --name-only HEAD~1,CI 环境里一般能直接取)。它的作用是帮 AI 区分"红的模块刚好本轮被改过 → 更像产品真回归"还是"产品没动 → 更像脚本或环境问题"。不给也不影响分诊,需要时它会找你要。

还缺的现场材料,让 AI 看完第一轮自己点名:

如果一次失败量大到这些材料塞不下,或者想做成无人值守自动分诊,再让 AI 写个十几行的脚本解析 JUnit XML、按用例汇总失败清单并关联截图挂到 CI;日常几十条的规模,直接贴日志和截图最省事。

2.2 一个提示词,分诊 + 聚类 + 根因一次出

下面是本轮 CI 的失败材料:每条用例的错误类型、堆栈和重跑结果(日志),
以及每条失败的界面截图(可能还附了本次改动的文件列表,有就参考)。
请结合日志和截图一次性完成分诊,本轮不要改代码。

第一步·分诊:每条失败输出一行——
  用例名 / 类别(真缺陷·flaky·环境数据·脚本问题 四选一)/ 置信度(高·中·低)/
  判定依据(引用重跑结果、错误类型、堆栈首行、与 commit 的关联)/ 建议动作。

第二步·聚类:把 flaky 按根因标签(登录态、等待时序、数据耦合、选择器、
  第三方接口、并发抢资源、时间日期、动画iframe…)聚类,多条同根因的归为一组。

第三步·对每个 flaky 簇直接给出:
  最可能的根因 + 证据、推荐修法方向、还缺什么证据(证据充分就写"可直接修")。

最后给处理优先级:哪些必须人立刻看(疑似真缺陷、低置信),哪些可批量按修法处理。

铁律:
- "证据支持的判断"和"你的推测"分开标注;证据不足不许硬判,明确列出要我补的材料;
- 禁止把 time.sleep、retry、force=True、try/except 吞异常当作修法,那是掩盖。

2.3 它交回的长这样

【必须人立刻看】
- test_pay_amount   真缺陷/高  断言金额 99≠100,本轮 commit 碰了计价逻辑 → 拦截提bug
- test_refund_flow  flaky/低   堆栈被截断、无 trace → 请补这条的 trace 和用例代码

【可批量处理·flaky 簇:登录态过期(6 条:test_enroll/test_coupon/…)】
- 现象:均 401 后跳登录,重跑通过,夜间跑高发
- 根因:storage_state 里的 token 过期,套件后半段才轮到的用例踩中
- 修法:登录态定时保鲜 + 并行按 worker/角色分配账号(详见 3.2)
- 结论:证据充分,改一处公共登录 fixture 可清 6 条,可直接修

【环境问题(8 条)】
- 均卡在支付沙箱超时,第三方沙箱故障 → 等恢复重跑,非产品问题,无需改码

【脚本问题(4 条:test_order_list/…)】
- 均 TimeoutError 必现找不到 .btn-submit,产品未改、本轮重构过下单页 → 定位器失效,改测试代码

价值一眼可见:二十条红灯,一轮被压成"1 条要立刻看的真缺陷 + 1 处封装改动清 6 条 + 一批等着恢复的环境故障 + 4 条改定位器的脚本问题 + 1 条待补料的疑难"。 人从逐条翻日志,变成只处理"真缺陷"和"低置信"两小撮。

边界:AI 的分类是分流建议不是判决。疑似真缺陷、要自动建 bug 单的,必须人确认。它把有明确信号的大部分快速归位,人集中火力在少数拿不准的上。

刚开始不信它很正常:头一两周先让 AI 分、人照常独立判一遍,再对照两者差异——重点不是看对错总数,而是看它在哪些根因上稳、哪些上飘。等你摸清它的可靠边界(通常登录态、选择器、环境这类有硬信号的很稳,跨模块数据耦合、罕见时序偏飘),就可以只复核"真缺陷 + 它自己标中低置信度"的那一小撮,其余直接按它的分流走。信任是这样一格一格放的,不是第一天全交。这个过程里,你每纠正它一次(团队怎么界定环境故障、哪些模块红灯必属真缺陷、哪类报错直接判脚本问题),就把这条口径回填进提示词或判定规则,它下一轮就更贴你的标准——分诊助手就是这样被你慢慢训练成可靠伙伴的。

三、少数疑难 flaky:补料、复现、验收

大部分高置信 flaky 在第二章已经拿到根因和方向。只剩证据不足、需要深挖的少数,走这一段——注意是"少数",不是每条都来一遍。

3.1 它点名、你补料

按它列的清单补证据:trace 可疑步(取法见 2.1)、相关 Page Object/fixture/factory 代码、git diff、这条的历史红绿规律等。补回后让它在已有分诊结论上继续,不用从头再来。

根因就按下面这张地图对号入座(和分诊聚类用的标签一致),绝大多数 flaky 落在:

类别 典型症状 快速判据
等待 / 时序(最高发) timeout、element detached 红在找/点元素,慢机或并行更易红
数据 / 状态耦合 单跑绿、整套红、隔天红 依赖残留数据、用例顺序、共享可变状态
登录态 / 会话 偶发跳登录、401、被踢下线 token 过期、storage_state 失效、同账号互踢
选择器 偶尔匹配 0 个或多个 动态 class、下标 xpath、文案会变
网络 / 第三方 接口超时、限流 红在等某接口,重跑常过
并发抢资源 只并行时红 固定账号、文件、端口、共用一条数据
时间 / 时区 / 日期 跨天、月底才红 用例里用了 now()、今天日期
动画 / 遮罩 / iframe / 新窗口 可见但点不到、句柄丢 系列前文讲过的传统老坑

3.2 按根因逐类确认和修

下面按根因地图逐类给"怎么确认 + 怎么修",可以让 AI 对着那一簇失败逐行给修法候选和影响面,人来拍板。

① 数据、登录态——根子在公共层,改在 fixture/factory,别在红用例上补。 这两类不一定出现次数最多,却往往一改清一片:让 AI 把这一簇引用的公共方法读出来,定位是哪一个的问题、改它、再回归整簇。它在这里值钱的不是"帮你写个工厂"(那点代码人手写更快),而是顺着一簇红灯找到公共根因、说清影响面。

数据先选对造数方式,运行时主流就这几种,稳定性取舍不同:

造数方式 做法 稳定性取舍
内存直接构造 代码里 new 一个对象,不入库 最快、零耦合,但绕过真实校验和 DB 约束,只适合纯逻辑用例
数据工厂 / Builder(factory_boy、Faker) 声明式生成字段、自动带唯一值 常规实体造数的主力,天然唯一、易清理,治耦合常用
调依赖接口造数据链 按业务顺序调 API 建完整链(建分类→建课程→上架→可报名) 最贴近真实、走过真实校验;fixture 里的 factory 多是这一种
DB 直插 / ORM 绕过接口直接写库 快,但绕过校验,数据容易和真实业务状态漂移
共享 seed 套件跑前统一灌一份基线数据 只适合只读基线;可变数据共用 seed,就是数据耦合的温床

选型口诀:可变数据用"工厂 / 依赖接口"现造,带唯一标识、跑完逆序清理;只读基线才用共享 seed。 把业务对象和接口给 AI,它能批量生成工厂和数据链,也能反过来扫现有用例,揪出误用共享可变数据、漏清理的地方。

登录态守四条:每个角色登录一次复用会话,别让每条用例走 UI;token 定时保鲜,否则套件后半段集体踩过期;并行按 worker 分账号,防同账号互踢;注入要放对地方——cookie 用 add_cookies/storage_state,token 存 localStorage 的用 add_init_script。

这里有个高频真坑:别图省事用 set_extra_http_headers 给每个请求塞 Authorization 来"伪登录"。这个头确实会随页面每个请求(含导航)发给后端,但它只骗得过后端鉴权,前端并不知道你登录了——现代 SPA 的登录态由 cookie/localStorage 里的 token 和用户信息驱动,路由守卫看本地没有登录态照样把你踢去登录页,一刷新、一跳转就露馅,表现就是"接口调得通、页面却没登录"。让 AI 改公共登录方法时最容易顺手用头伪登录,审查时盯一眼。

② 其余六类——大多改在用例或 PageObject 层。

类型 怎么确认 怎么修
等待 / 时序(最高发) --count、-n auto、慢机器更易红;trace 里动作早于元素就绪或接口返回 用自动等待:expect(loc).to_be_visible()、断言最终文本 to_have_text();等接口用 with page.expect_response("**/api/x"): 包住触发动作。禁用固定 time.sleep;别缓存 element handle,detached 就重新定位
选择器 报 strict violation(匹配多个)或匹配 0 个;class 是哈希、用了下标 换稳定锚点:首选 get_by_test_id()(让研发补 data-testid),其次 get_by_role();多个时用语义 + 可见性收窄,别用 nth 下标硬凑
网络 / 第三方 红在等某个外部接口,trace 网络里超时/429,重跑常过 不依赖真实第三方:page.route 造桩返回固定响应(route.fulfill(json=...));自家接口慢就等响应/等 UI 终态,别 sleep
并发抢资源 只在 -n auto 红、单 worker 绿 共享资源全部按 worker/用例隔离:数据带唯一标识、独立账号、临时文件用 tmp_path、端口动态分配,不跨用例写同一条数据/文件
时间 / 时区 / 日期 跨天、月底、特定时区才红 后端/Python 侧注入时钟,用 freezegun.freeze_time("2026-12-31 23:59:30") 冻结(注意它冻不了浏览器里 JS 的时间,前端跨天逻辑要在页面里 mock 时钟);业务代码别散落 now(),由可注入的时钟提供;断言不写死"今天"
动画 / 遮罩 / iframe / 新窗口 元素可见但点不到、点了没反应、句柄丢 先等遮罩消失(expect(mask).to_be_hidden())再操作,测试配置开 prefers-reduced-motion;iframe 用 frame_locator() 限定作用域;新窗口用 with context.expect_page() as new_page: 拿句柄

3.3 怎么才算真修好

跑一遍绿什么都证明不了,四条验收:

  1. 有对照:修复前已能稳定复现红;
  2. 高压验证:在当初触发它的条件下(连跑、并行、加延时)全绿;
  3. 反向验证:回退修复,红应重新出现——证明你改的真是因果链上那一环;
  4. 撤掉遮羞布:当初若加过 retry/sleep,验证后删掉,用例依然稳。

3.4 AI 治 flaky 的四个坑

坑 表现 卡点
幻觉归因 讲个证据里没有的根因 强制引用证据行,事实/推测分列
诱导假修复 上来就 sleep/retry/force 提示词禁止,审查一票否决
一次绿当成功 跑一遍过就宣布修好 坚持高压 + 反向验证
脱离业务乱改 不清楚该时序业务上是否允许 根因确认和修复取舍由人拍板

证据聚合、假设排序、复现设计、修复候选归 AI;是不是这个根因、业务上怎么修才对,永远归人。

四、治已病,反哺治未病

分诊跑顺后,让 AI 定期把 flaky 根因标签做一次分布统计——哪类最常犯,哪类就是框架下一步最该长肌肉的地方:等待时序高发就强化 BasePage 自动等待、禁用裸 sleep;登录态成片挂就把保鲜和账号隔离做成默认;数据耦合多就收紧 factory。

于是咬成一个环:整份日志一次分诊,压低"看红灯"的成本;高发根因回到封装里修,减少未来的红灯。


写在最后

面对一屏红灯,最值钱的用法不是"帮我把这条红的修了",而是:整份日志一次喂给 AI,让它分诊、把 flaky 聚类并直接给根因和修法方向、同时告诉你还缺哪几条证据;人只拍板真缺陷和少数疑难。 判得透的当场修一片,判不透的补料、复现、反向验证;再把每类根因沉淀回框架,存量 flaky 越清越少、同类新坑在公共层就被拦住,套件的稳定性随之收敛。

一句话记住分工:AI 负责大规模地"看"和有依据地"猜",人负责"判"和"拍板"。


如果这篇对你有启发,欢迎转发给那个"CI 一红就重跑、重跑绿了就合入、每天耗在逐条翻日志上"的团队。


↙↙↙阅读原文可查看相关链接,并与作者交流