跳到正文
原文
Playwright实战教程· 欢乐马·· 9天前AI 评分69

Playwright Agent 实战教程:用 Healer 自动修复报错用例

原文标题:Playwright Agent 实战教程:AI修报错用例

AI 导读

Playwright Agent 的 Healer 能完成跑测试、诊故障、改代码、重跑一整轮,在 VS Code 的 Copilot Chat 里切到 Agent Mode、选 @playwright-test-healer 并贴一句 Run and fix failing tests 即可触发。

正文

正文配图

正文配图

用例一改版就红?半夜被 CI 叫醒改断言?Playwright Agent 官方 Healer 实战演示:跑测试、诊故障、改代码、重跑,一条龙。手把手教程 + 一套可迁移的故障判断法,看完就能用在自己项目上。


一、测试的成本不在写,在养

写自动化测试的小伙伴有体会,修改用例成本比单独写用例高多了。Playwright Agent有个Healer 干的就是这活:跑测试 → 诊故障 → 改代码 → 重跑。

说下我的心得:它修不动时,会标记 test.fixme()停下来说"这是产品 Bug",人工再介入而不会只是删断言让测试变绿。


二、跟着做完,你能拿到

  • 让 Healer 跑完「跑 → 修 → 重跑」完整一轮

  • 一套故障判断法

    拿到红灯先问哪 3 个问题,才知道该改测试还是报 Bug

  • 三类故障的修法对照(断言过期 / 异步时序 / 产品 Bug)

  • 一套敢落地的安全护栏(本地修 + 人审 diff + 不自动 push)


三、准备:先给项目造一条红灯

Healer 要有东西可修,不用另找项目——你自己的用例库就是最好的素材。

按下面任一种方式造一条,跑一遍确认能稳定复现:

① 造「断言过期」(最常见)

把某条断言的期望值,改成一个已经被改掉的旧文案:

typescript

// 页面现在写的是「小观察价」,断言还停在旧的「观察价」
awaitexpect(page.getByText('观察价', { exact: true })).toBeVisible();

② 造「异步时序」

在产品数据加载完之前就断言,不给等待:

typescript

await page.goto('/your-page');
const text = await page.locator('body').innerText();   // 拍一张快照立刻断言
expect(text).toContain('已加载');

③ 造「功能未实现」

挑一条还没做的产品需求,先把用例写上:

typescript

awaitexpect(page.getByRole('button', { name: /导出 CSV/ })).toBeVisible();

跑一遍,记住它红在哪一行,第五节要对照:

bash

npx playwright test your-broken.spec.ts --reporter=list
  ✘  1 … 结果区应显示观察价 (5.0s)
  ✘  2 … 快照列表应在打开后可见 (2.0s)
 2 failed

💡 macOS 12 / 13 装不上 Chromium → 加 PW_CHANNEL=chrome驱动本机 Chrome(见第 1 篇)。


四、关键动作:一句"跑并修好"

四步走:

  1. VS Code 打开项目,Copilot Chat切到 Agent Mode

  2. 选代理 @playwright-test-healer

  3. 把红灯的用例文件加进上下文

  4. 贴这句(可直接复制)

Runand fix failing tests

它随后自行跑完一整轮。终端里你会看到它是边取证边判断的:

[跑]   ✘ 结果区应显示观察价 → getByText('观察价') 找不到元素
[取证] 打开失败快照 → 结果区实际文案是「小观察价 / 大观察价」
[归因] 文案改版导致断言过期,不是产品 Bug
[改]   断言更新到当前文案,保留校验语义
[重跑] ✓ passed

中间那个「归因」才是重点——它凭什么判断"这是断言过期"而不是"产品坏了"?这就是下面要讲透的东西。


五、先分诊,再动手(本篇最值钱)

第一件事不是改代码,是先定位问题:

问自己 3 个问题,答案直接决定改法:

  1. 页面上实际是什么?

    —— 打开 trace / 失败快照,看真实文案与真实元素

  2. 它是稳定红还是偶发红?

    —— 连跑 3 次:次次红=断言错,偶发红=时序问题

  3. 产品到底有没有这个功能?

    —— 全页搜一遍,没有就是功能没做

拿到答案,再对号入座:

  • 实际内容变了 → A 断言过期→ 更新断言

  • 时红时绿 → B 异步时序→ 改成等条件

  • 功能压根没有 → C 产品 Bug→ fixme 转人工

下面三种,就是这套分诊的完整落地。

A|断言过期 → 更新到当前文案,但不许删断言

typescript

// ❌ 修复前:文案已从「观察价」改成「小观察价 / 大观察价」
awaitexpect(page.getByText('观察价', { exact: true })).toBeVisible();
// ✅ 修复后:跟上当前页面,校验语义一点没丢
awaitexpect(page.getByText('小观察价', { exact: true })).toBeVisible();
awaitexpect(page.getByText('大观察价', { exact: true })).toBeVisible();

审这个 diff 的标准:断言跟上了当前文案,而且"结果区必须给出观察价"这个校验意图还在。

如果它只是把断言删掉让测试变绿——这个 diff 不合格,打回去重修。这是 Healer 最容易滑过去的地方,也是你审 diff 的第一优先级。

B|异步时序 → 等条件,不要等时间

typescript

// ❌ 修复前:innerText 拍一张快照就断言,数据还没回来
const text = await page.locator('body').innerText();
expect(text).toContain('已加载');
// ✅ 修复后:等条件出现
awaitexpect(page.getByText(/已加载 \d+ 条/)).toBeVisible({ timeout: 20_000 });

区别只有一句话:

  • waitForTimeout(2000)

    —— 睡死。机器快时白等,机器慢时照挂,是毒药

  • expect(...).toBeVisible()

    —— 自带轮询重试,条件成立就过,快慢机器都稳

验收标准:改完连跑 3 次全绿才算修好(--repeat-each=3),一次绿不算数。

C|产品 Bug → test.fixme(),不是硬改成能过

typescript

// ✅ 修复后:测试逻辑不动,声明"功能没做,先排队"
test.fixme(true, '站点暂未提供「导出 CSV」按钮,已转产品确认(2026-09)');
awaitexpect(page.getByRole('button', { name: /导出 CSV/ })).toBeVisible();

fixme不等于跳过。跳过是"假装没看见",fixme 是"测试没错,功能有问题,先排队等开发"——报告里明确显示为待修复项。

每次看到 fixme,你先做的事:确认它到底是产品 Bug,还是测试理解错了。后者就自己改回来重跑,别让它躺在报告里长蘑菇。

三类修完,最终交付长这样:2 passed, 1 skipped。那条 skipped 不是失败,是你亲手签过字的待办。


六、敢不敢用?三道护栏

这是收官篇最该带走的。AI 改代码可以,但人类絮语把关。

  1. 本地修 + 人工审 diff

    — CI 红了,本地(或人工触发)跑 Healer,修完 git diff一条条看,再合入。不要让 Agent 自动 push。

  2. 禁区优先审

    — 金额、价格、业务公式相关的断言,review 优先级最高。这类断言被"改绿"的代价最大。

  3. fixme 必须转人

    — 每个 fixme 都要有人确认是产品 Bug 还是测试写错,不能自动消失。

接进 CI:只让它跑,不让它改

CI 的职责是跑 + 留证据,修复留给人 / Agent 在本地做:

yaml

-name:RunPlaywrighttests
run:npxplaywrighttest--reporter=html,list
-name:UploadPlaywrightReport(含trace/截图/视频,供Healer取证)
if:always()
uses:actions/upload-artifact@v4
with:
name:playwright-report
path:|
      output/html-report/
      test-results/
retention-days:7

红了 → 拉 trace / 视频 → 本地跑 Healer → 审 diff → 合入。


七、3 条心法,照做少踩坑

  1. 修完必看 diff

    — 重点看它有没有"删断言换绿",这是唯一的红线

  2. 偶发红要连跑 3 次

    — 一次绿不算绿

  3. fixme 是待办不是垃圾桶

    — 每个 fixme 都要有归属


八、新手最常问的 3 个问题

Q1:它会把产品 Bug 误判成测试问题、硬修掉吗?岗位说明书里写死了流程:先复现 → 取证(快照/控制台/网络)→ 归因 → 只改该改的 → 重跑验证,修不动且有把握测试正确时用 fixme 停下。再加你人工审 diff,双重保险。

Q2:Python / .NET 项目能用吗?目前官方围绕 TypeScript(@playwright/test)提供得最完整。但"计划 → 生成 → 修复"这套思路不绑语言,先用 TS 工程把流程跑通验证价值。

Q3:fixme和"跳过"有什么区别?test.fixme()= "测试本身正确,但功能有问题,先不跑",报告里明确标为待修复项;跳过是"假装没看见"。fixme 会一直提醒你,跳过不会。


九、小结 & 系列收官

  • Healer = 跑 → 诊 → 修 → 重跑,把"养测试"自动化

  • 分诊三问:实际是什么 / 稳定还是偶发 / 功能有没有→ 对应三种改法

  • 你的角色没变,只是从"改代码"变成"审 diff"

三篇走完,Planner 定测什么 → Generator 写代码 → Healer 保不腐,闭环成立。

回头看这套东西真正改变的不是效率,是角色:你不用再一行行敲用例,但"测什么"和"这样改对不对"这两道闸,永远在你手里。

AI 能替你写一百条用例,替不了你判断哪条值得写。这就是测试架构师在 AI 时代的位置。


来源:Playwright实战教程 · mp.weixin.qq.com