Skip to content
Original
Playwright实战教程· 欢乐马·· 9 days agoAI score69

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

Original title: Playwright Agent 实战教程:AI修报错用例

The title and summary in the selected language are awaiting translation.

AI overview

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

Full text

The full text in the selected language is awaiting translation. The original is shown for now.

正文配图

正文配图

用例一改版就红?半夜被 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 时代的位置。


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