想象这样一个场景:你用 Codex 重构了一个前端组件,把按钮上的文案从"保存草稿"改成了"暂存"。Codex 很高效,三分钟搞定了组件代码、样式调整和国际化文件。你检查了一下,没问题,提交,push。

然后 CI 炸了。

Playwright 测试报错:locator('button:has-text("保存草稿")') 找不到元素。这本该是一个无关紧要的选择器漂移——按钮还在,功能没变,只是文案改了。但 CI 红了,你的自动化流程卡住了。

更糟糕的事情在后面。你让 Codex 去修这个失败的测试。Codex 很"聪明"——它读取了 Playwright 的错误日志,看到了新按钮的文案是"暂存",然后它不只是修了选择器,还顺手调整了测试里的断言逻辑:把 expect(page).toHaveText("草稿已保存") 改成了 expect(page).toHaveText("内容已暂存")。测试绿了。Codex 骄傲地告诉你"修复完成"。

但那个断言本应该失败的——后端 API 返回的文案实际上还是"草稿已保存",因为后端的国际化没有跟着改。Codex 把真正的 bug 给"修绿"了。

这正是 AI 编程时代的测试困境:Agent 对测试的"修复"能力越强,它掩盖真实回归问题的风险就越大。

自愈测试的两层逻辑#

Ruslan 在 7 月 10 日的 Show HN 上发布了 9lives,一个专门为解决这个问题而设计的自愈测试运行器。它的核心设计哲学可以用一句话概括:选择器漂移该修,行为变化不该动。

9lives 把测试修复分成了两级:

Tier 1(离线、确定性、免费):当 Playwright 测试因为选择器找不到元素而失败时,9lives 会读取 Playwright 在失败瞬间捕获的页面快照(page snapshot),然后按照一个优先级链重新定位元素:data-testid → id → aria-label → text → class。整个过程不需要 LLM,不联网,不需要 API 密钥。绝大多数选择器漂移在几秒内就能修复,零成本。

这里有一个值得注意的技术细节:与 Healenium 等传统的自愈方案不同,9lives 不需要"之前的绿跑快照"作为基线——它完全从失败本身出发,分析失败时刻的 DOM 快照来找到最稳定的定位锚点。这意味着即使你的测试第一次跑就失败了(比如 CI 中全新 clone 的项目),它也能工作。

Tier 2(在线、需要 Agent):当变化超出了选择器层面——比如你重构了一个表单的 DOM 结构,从 <div> 改成了 <section>,整个测试的交互流程都可能需要调整——Tier 1 不再适用。这时 9lives 会启动你本机安装的 coding agent(Codex、Claude Code、OpenCode)来理解和修复这些结构性变化。关键设计是:它用的是你已经付费的 Agent 订阅,不需要再申请新的 API 密钥。

“拒绝修复"而非"强制修复”#

9lives 最让我印象深刻的设计决策不是它修了什么,而是它拒绝修什么

当一个测试的断言失败时——也就是说,程序的输出或行为真的变了——9lives 会标记为"需要人工介入"然后停下来。它不会去猜测新的断言应该是什么,不会用 LLM 推断预期的业务逻辑,不会为了让测试变绿而改动一行断言代码。

这个设计的核心洞察是:选择器失败是技术问题,断言失败是业务问题。 前者可以用算法解决,后者必须有人参与决策。把断言改绿让 CI 通过,不是"修复测试",而是"掩盖回归"。

Ruslan 在他的 Show HN 帖子里写得很直白:“市面上很多自愈工具 green-wash 了真实的失败。“这个词选得很精准——green-wash,像漂绿一样把测试"漂绿"了。

在 Agent 工作流中的定位#

9lives 不是一个孤立的测试工具,它被设计成 Agent 工作流的一个环节:

  • 9l mcp:一个零依赖的 MCP 服务器,Claude Code 和 Cursor 可以在会话中途调用 heal_test——当 Agent 重构代码后触发了测试失败,它可以自己修复选择器漂移(Tier 1),而不是去改断言。
  • 9l watch:文件保存时自动运行——Agent 每写一个文件,测试自动检查和修复选择器。
  • 9l report:读取本地修复历史,告诉你哪些选择器反复腐烂,应该用什么定位策略替换。

想象一下理想的工作流:Codex 完成了 10 个文件的改动,触发了 5 个测试失败。其中 4 个是文案修改导致的选择器漂移,1 个是真正的回归。不用任何人工干预,9lives 修复了 4 个选择器问题,把第 5 个断言失败标记出来——“这个你自己看”。你只需要审查那 1 个真正重要的失败,而不是在 5 个红色告警中搜寻。

“自愈"的真正边界#

9lives 的设计并非完美。它的 Tier 1 严重依赖 Playwright 的失败快照功能,Cypress 和 Selenium 的离线自愈能力就弱很多,更多依赖 Tier 2 的 LLM 修复。另外,Tier 2 把失败页面的内容送进 LLM 是一个需要谨慎对待的安全边界——如果你在测试中使用真实数据,那些敏感信息会成为模型输入。

但从更宏观的视角看,9lives 提出的分级自愈策略——能离线确定的就离线确定,需要 AI 的地方用你已经信任的 Agent,而最危险的那一层(断言变更)直接拒绝——代表了一种务实的安全哲学。它既不是"禁止 Agent 碰测试"的保守派,也不是"让 LLM 全权负责"的激进派,而是画了一条清晰的线。

这条线很重要,因为它回答了一个更根本的问题:在 AI 编程的时代,我们到底应该在哪些环节信任 Agent 的判断,在哪些环节坚持人类监督? 或许答案不是"信任"或"不信任"二选一,而是像 9lives 那样,在每一层定义自己的不可逾越的底线。


参考来源