作者认为Self-healing测试在掩盖问题而非解决问题——修复过的定位器可能指向错误元素,产生静默的假阳性。提出真正的测试资产应该是自然语言测试用例,而非脚本本身。
Self-healing tests 正在解决一个错误的问题。
当一个"治疗师"把一个坏掉的 locator 替换成"差不多"的元素时,测试运行保持绿色,但这个测试现在可能正在断言一些没人想要的东西。你没有修复测试。你只是把一个刺耳的失败换成了一个安静误报。
我反复回到的这个替代方案是:根本不维护脚本。如果模型可以读取测试用例并自己执行浏览器操作,那脚本就不再是你需要的东西了。测试用例才是。你无法破坏一个根本不存在的脚本。
Self-healing locator 回答的是"按钮移走了,我怎么还能点击它?"它从不问"这个脚本是否还应该存在?"
更糟糕的是,治疗师可以把一个坏掉的 selector 换成附近一个看起来合理的元素。测试通过了,但它现在检查的是作者从未想过要检查的东西。你把一个刺耳的失败换成了一个安静的误报,而这是最昂贵的测试债务类型。一个持续损坏的 locator 也在告诉你一些事情(不稳定的 hooks、频繁变化的页面),而自动修复会屏蔽这个信号。
这就是我反复回到的替代方案。你的真相来源是纯语言的测试用例,就是你交给人工测试人员的那种。你不需要把它转换成需要有人维护的 Playwright 脚本,而是让 AI agent 读取它并执行它。
使用 Playwright MCP server 这样的浏览器控制工具,agent 可以打开无头浏览器、跟随步骤、查看页面并报告发生了什么。因为它基于页面当前的状态工作,所以没有什么需要"治疗"的。如果按钮移走了,它会找到按钮,就像人类一样。
这个世界里的测试用例就是这样写的:
## TC-CART-014: Promo banner shows for a new promo SKU
Precondition: logged in as a standard test user, cart is empty
1. Add the item with SKU PROMO-001 to the cart
2. Open the cart page
Expected: a promo banner is visible above the item list and mentions the discount
Also check: no error toast, page has no layout overlap on mobile width
给 agent 的指令同样简短:
Run TC-CART-014 against the staging URL in a headless browser.
For each step, note what you did and what you saw.
Take a screenshot at the end. Report PASS/FAIL per expected result, with evidence.
输出是一份带截图的简短报告。你目视验证。人类几秒钟就能浏览完证据,这通常比调试一个红色 CI 任务更快。不存在脚本,所以不存在脚本腐坏的问题。
大多数测试套件都有一条长尾的检查,它们只在某个时刻重要:一次发布、一次迁移、一次有风险的重构、一个你想要复现的单独 bug 报告。为这些编写和维护脚本是一个糟糕的权衡。
这是我最喜欢的部分:这不是一扇单向门。那些后台运行可以兼作你维护套件的发现管道。
当一个场景反复出现、捕获了真实 bug、或者守护着关键的东西,这就是一个信号——它值得一个proper的确定性脚本。到了这个阶段,你就提升它:把 agent 做的事(步骤、它找到的 selector、它需要的状态)转化为一个有真正断言的已审查测试。
import { test, expect } from '@playwright/test';
test('promo banner shows for a new promo SKU', async ({ page, request }) => {
const res = await request.post('/api/cart', { data: { sku: 'PROMO-001', qty: 1 } });
expect(res.ok()).toBeTruthy();
const { cartId } = await res.json();
await page.goto(`/cart/${cartId}`);
await expect(page.getByTestId('promo-banner')).toBeVisible();
});
我的规则:如果一个脚本坏了,你不会紧急修复它,就不要提升它。其他一切保持为 agent 按需运行的测试用例。你的维护套件保持小而可信,因为里面的每个脚本都是被挑选出来的,而不是累积出来的。
我不想过度吹捧。一些诚实的限制:
它们不是合并门禁。Agent 运行是非确定性的,比编译后的脚本慢,并且消耗 token。两次运行可能走不同的路径或对"可见"做出不同的判断。适合探索和发布检查,不适合千量级测试的回归运行。
Agent 可能过于宽容。一个"绕过"了坏流程的 agent 可能会报告通过,而真实用户其实已经被卡住了。编写严格的预期结果,并仔细阅读证据。
核心流程需要代码。审计和合规追踪需要确定性、持久的脚本。
测试数据和环境知识仍然来自你。AI 不知道你的种子账户或 staging 环境的小毛病,除非你告诉它。
有些失败是真实的 bug。永远不要通过重试让它变绿。
Self-healing 优化的是让每个脚本存活。一次性方法优化的是让正确的脚本存活:让 AI 读取你的测试用例、无头运行它们、目视验证证据,只有当一个场景证明了它的价值时才把它变成维护代码。
作为 SDET,你的价值不在于你维护了多少脚本。而在于判断哪些检查值得变成代码。
所以我的问题是:你当前套件中的哪一部分可以被一个写得好的测试用例和一个 agent 取代,你在信任它之前需要看到什么?
Originally published at luthfiferdian.com.