Playwright 在 1.56 版本(2026-05)正式内置 AI 测试组件 Playwright Test Agents。它把"写 E2E 测试"这件事拆给三个智能体分工:planner 定计划、generator 写代码、healer 修 bug。

1. Test Agents 是什么
先说官方定位(避免和圈内其他"AI 测试工具"混淆):
-
版本 :v1.56+ 内置,不是第三方插件,装
@playwright/test就有 -
启用 :
npx playwright init-agents --loop=codex,生成三个 TOML 定义文件(--loop用于指定对接的 AI Agent 客户端类型:codex / claude / vscode / opencode,不同值输出到对应客户端目录,实测 codex 落在.codex/agents/) -
依赖 :所有 agent 都跑在同一个 MCP server 上(
npx playwright run-test-mcp-server)------注意这是 Test Agents 专属 MCP 服务 ,和独立包@playwright/mcp不是同一个程序,别混淆
三件套的职责和沙箱权限:
| Agent | 职责 | 产出 | 沙箱权限 |
|---|---|---|---|
| planner | 探索站点、设计测试计划 | test-plan.md 测试计划 | read-only |
| generator | 按计划逐条生成测试代码 | tests/*.spec.ts | read-only |
| healer | 运行并修复失败的测试 | 修好的测试代码 | workspace-write |
权限设计:planner 和 generator 在 MCP 沙箱里配置为 read-only------即不授予文件写入工具能力 (注意这是 MCP 工具权限,不是操作系统文件只读),只有 healer 拥有 workspace-write 工具权限。这是刻意的------让 AI 先看、先写草案,最终能不能落盘、怎么改,由 healer 这一个"有写权限的 agent"把关,降低 AI 乱改代码的风险。
2. 底层逻辑:它到底是怎么工作的
Test Agents 不是把截图丢给 LLM 猜,而是通过 Model Context Protocol(MCP) 把 LLM 与真实浏览器连起来,分三层协作。

2.1 三层架构
第一层:Playwright engine(引擎层)。基于 Chrome DevTools Protocol(CDP)的浏览器自动化,和标准 Playwright 测试完全同源。这保证了 agent 产出的东西就是"真测试",不是脚本玩具------能跑、能断言、能进 CI。
第二层:LLM layer(决策层) 。用大模型(GPT、Claude 这类)解释 DOM 结构、页面路由和应用行为。这一层的关键设计是:模型读的是结构化的 accessibility snapshot(无障碍快照),不是原始截图。
第三层:Orchestration loop(编排循环)。协调引擎和模型的交互,循环反复:发送页面上下文 → 接收模型指令 → 执行浏览器动作 → 重复直到任务完成。
2.2 为什么用无障碍快照,而不是截图
老式的 AI 测试工具大多靠截图:模型看一张渲染出来的图,"猜"页面上有什么。但截图有天然的视觉歧义:
-
按钮是不是被弹层挡住,截图给不出确定答案
-
两个长得一样的 "Submit",分别属于哪个表单,截图无法区分
AOM(Accessibility Object Model)换了个思路:不给像素,给语义。每个元素提供干净的结构化信息:role、name、state、层级位置。比如 Role: button, Name: Checkout 这个描述,远比 div.checkout-btn-v3 稳定------前端改样式它不受影响。
顺带一个有趣的巧合:ARIA roles 和 labels 本来是给屏幕阅读器这类辅助技术设计的,结果恰好对 AI agent 也完全适用。为残障用户设计的语义标准,成了喂给大模型的最好格式。
3. 拿抖音实测:在 codex 里把三件套跑通
3.1 测试目标与版本
选抖音测试是因为它比其他 demo 站更能暴露 agent 的能力边界:公开内容多、交互复杂(推荐流、视频详情、评论、搜索、登录墙)、有真实反爬。
环境与版本(实测自测):
| 项 | 值 |
|---|---|
| 工作目录 | D:\Workbench\playground\playwrightagents |
| Playwright | 1.62.1(@playwright/test ^1.62.1) |
| 系统 | Windows 11 |
| 测试目标 | www.douyin.com/ |
| 编排方式 | 全部 agent 在 codex 对话中执行 |
3.2 反爬和登录态
3.2.1 反爬结论
抖音站点 headless 必被拦,不管带不带登录态:
| 打开方式 | 结果 |
|---|---|
| headless 无痕 | 验证码中间页(被拦截) |
| headless + 登录 profile | 验证码中间页(被拦截) |
| headed + 登录 profile | 正常进入 |
想测抖音,必须 headed + 已登录 profile。这个结论直接决定后面 codex 的引导方式------必须在对话里明确让 agent 用有头模式。
3.2.2 登录态是怎么生成的:saveprofile.js
要给测试"已登录"身份开局,我用了一个独立的 saveprofile.js 脚本:打开真实浏览器 → 手动扫码登录 → 把登录态导出成 storageState 文件。完整脚本:
js
const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');
const PROFILE_DIR = "douyin-profile";
const STATE_FILE = path.join(PROFILE_DIR, "state.json");
(async () => {
if (!fs.existsSync(PROFILE_DIR)) fs.mkdirSync(PROFILE_DIR, {recursive:true});
const browser = await chromium.launch({headless:false});
let context;
if(fs.existsSync(STATE_FILE)){
context = await browser.newContext({storageState:STATE_FILE});
}else{
context = await browser.newContext();
}
const page = await context.newPage();
await page.goto("https://www.douyin.com");
if(!fs.existsSync(STATE_FILE)){
console.log("请手动扫码登录,登录完成按回车保存状态");
await new Promise(resolve=>process.stdin.once('data',resolve));
await context.storageState({path:STATE_FILE});
console.log("登录态已保存");
}
await new Promise(resolve=>process.stdin.once('data',resolve));
await browser.close();
})();
执行过程(实测现象):
node saveprofile.js
→ 弹出 Chromium 浏览器窗口,打开抖音网页版
→ 控制台提示:请手动扫码登录,登录完成按回车保存状态
→ 浏览器扫码登录抖音网页版,确认账号已经登录成功
→ 回到终端窗口,按一下回车键
→ 自动创建 douyin-profile 文件夹,生成 state.json
→ 再次按回车,浏览器关闭
要点:脚本会复用已有的 state.json(storageState 方式),cookie 过期后重跑一次即可,不需要改任何测试代码。
踩坑(本版实测新增) :虽然后面在 codex 里明确指定了 douyin-profile 作为 profile 目录,抖音还是会跳出登录、要求验证手机号后才进入登录态------登录态方案对强反爬站点是有边界的,这条后面在 codex 里还会遇到。
3.3 按步骤实测:六步跑通三件套
Step 1:安装最新版 Playwright
bash
npm install -D @playwright/test@latest
npx playwright install chromium
Step 2:初始化 agents(init-agents --loop=codex)
bash
npx playwright init-agents --loop=codex
实测输出(原样):
sql
PS D:\Workbench\playground\playwrightagents> npx playwright init-agents --loop=codex
🎭 Using project "" as a primary project
📝 specs\README.md - directory for test plans
🌱 seed.spec.ts - default environment seed file
🤖 .codex\agents\playwright_test_generator.toml - agent definition
🤖 .codex\agents\playwright_test_healer.toml - agent definition
🤖 .codex\agents\playwright_test_planner.toml - agent definition
✅ Done.
这一步生成三件套的定义文件 + seed 测试:
-
seed.spec.ts:所有 agent 活动的基础起点 -
.codex/agents/playwright_test_planner.toml:planner 的定义 -
.codex/agents/playwright_test_generator.toml:generator 的定义 -
.codex/agents/playwright_test_healer.toml:healer 的定义 -
specs\:计划文件目录(init 声明,计划实际由 codex 生成在根目录)
每个 TOML 都声明了角色、沙箱权限,以及它依赖的 MCP server。以 planner 为例(真实文件摘录):
toml
name = "playwright_test_planner"
description = "Use this agent when you need to create comprehensive test plan for a web application or website"
sandbox_mode = "read-only"
# ...developer_instructions...
[mcp_servers.playwright-test]
command = "cmd"
args = ["/c", "npx", "playwright", "run-test-mcp-server"]
enabled_tools = ["browser_click", "browser_snapshot", "browser_navigate",
"browser_evaluate", "planner_setup_page", "planner_save_plan", "..."]
generator 的 enabled_tools 里有 generator_setup_page / generator_read_log / generator_write_test;healer 的是 test_run / test_debug / test_list / test_generate_locator,且 sandbox_mode = "workspace-write"------三件套里只有它具备写权限。
Step 3:配置 seed 测试
修改 seed.spec.ts,把起点指向抖音:
ts
import { test, expect } from '@playwright/test';
test.describe('Test group', () => {
test('seed', async ({ page }) => {
await page.goto('https://douyin.com');
});
});
Step 4:运行 Planner(在 codex 中执行)
在 chatgpt 新建 codex 项目,输入:
使用 douyin-profile 作为 profile目录,使用seed.spec.ts作为基础探索抖音网站生成测试计划
实测现象(本版重点踩坑):
- 必须引导有头模式:planner 默认倾向 headless,但 3.2.1 已经验证过抖音 headless 必被拦------需要在对话里明确要求它用真实浏览器窗口
- 即使指定了 profile,抖音仍会跳出登录:要求验证手机号后才进入登录态,这个环节需要人工介入一次
- planner 用真实浏览器逐步探索页面(精选/推荐/关注/搜索/视频详情/我的等),最终生成
test-plan.md
产出的 test-plan.md 按功能域编号的完整计划(真实文件摘录):
markdown
# 抖音网站测试计划
## 探索基线
- 基础用例:`seed.spec.ts`
- 入口:`https://douyin.com`
- Profile:`douyin-profile`
- 已复用状态:`douyin-profile/state.json`
- 探索结果:有头模式访问后进入 `https://www.douyin.com/jingxuan`,页面可正常加载精选内容。
## 阻塞与前置条件
1. 使用真实浏览器打开 `douyin-profile`;若出现验证码,由人工完成拖动拼图。
2. 如需播放、互动或搜索,先完成登录并保持同一登录态。
## 测试范围
| ID | 场景 | 核心检查 | 优先级 |
|---|---|---|---|
| ACC-01 | 首页访问 | 页面成功加载;主导航、推荐内容、搜索入口可见 | P0 |
| ACC-02 | 验证码拦截 | 触发验证时停留在验证页;刷新不造成死循环 | P0 |
| NAV-01 | 主导航切换 | 推荐、关注等频道切换后 URL、标题和内容区同步变化 | P1 |
| FEED-01 | 推荐流浏览 | 首屏视频卡片可见;滚动加载新内容 | P0 |
| FEED-02 | 视频播放 | 播放区域出现;暂停/继续/音量/进度操作正常 | P1 |
| SEARCH-01 | 关键词搜索 | 登录弹层关闭后输入关键词并提交;结果页展示 | P0 |
| SEARCH-02 | 搜索结果筛选 | 视频/用户等筛选项可切换 | P1 |
| VIDEO-01 | 视频详情 | 详情浮层打开;互动区和评论列表可见 | P0 |
| USER-01 | 用户主页 | 头像、昵称、作品列表加载;验证作者跳转规则 | P1 |
| AUTH-01 | 登录态 | 一键登录后完成登录;退出后回到未登录状态 | P0 |
| ACTION-01~04 | 点赞/关注/评论/分享 | 状态与数量变化;不误触发外部发布 | P1/P2 |
| MINE-01/02 | 我的主页/内容管理 | 个人信息、收藏、观看历史等入口可用 | P1/P2 |
| FOLLOW-01/02 | 关注页 | 关注列表、直播中列表、视频控件可用 | P1 |
| ERROR-01 | 弱网/接口失败 | 可理解的失败状态和重试入口 | P1 |
| RESP-01 | 响应式布局 | 1440×900、768×1024、375×812 下无关键控件遮挡或溢出 | P2 |
效果:计划把"探索基线"(入口、profile、已复用状态、实际确认的导航/入口/弹层行为)和"阻塞与前置条件"(人工验证码、测试账号隔离)都记录下来了------这正是 planner 的价值:把 QA 探索式测试的系统性观察固化成了可执行的测试范围。
坑 :planner 是 read-only,只出计划不动代码;而且计划里涉及登录的用例依赖登录态,所以跑 planner 之前必须先按 3.2.2 把 state.json 准备好。
Step 5:生成测试(在 codex 中执行)
拿到计划后,继续在 codex 对话里让 generator 干活,输入:
bash
请使用生成的测试计划来生成Playwright测试用例并把他们保存到 tests/checkout/
codex 反馈(原样):
已生成正式测试:douyin.spec.ts 共 10 条用例,使用 douyin-profile 持久化目录并以有头模式运行;9 条通过,1 条暴露 375px 横向溢出问题。 运行:npx playwright test tests/checkout/douyin.spec.ts --workers=1
产物是 tests/checkout/douyin.spec.ts(146 行),覆盖 ACC/NAV/FEED/SEARCH/VIDEO/USER/MINE/FOLLOW/ACTION/RESP 全部场景。关键代码(真实文件摘录):
ts
const test = baseTest.extend<{ profileContext: BrowserContext }>({
profileContext: async ({}, use) => {
const context = await chromium.launchPersistentContext(PROFILE_DIR, {
headless: false,
viewport: { width: 1440, height: 900 },
});
await use(context);
await context.close();
},
...
});
要点 :generator 生成的用例用 launchPersistentContext 复用 douyin-profile 目录(而不是在 config 里配 storageState),并以有头模式、1440×900 视口运行------这正是 3.2 反爬结论在代码层面的落地。
实测结果 :10 条用例里 9 条通过,1 条失败------375px 窄屏下页面横向溢出(RESP-01 响应式布局)。这不是测试写错,而是抖音站点的真实问题,正好留给 Step 6 的 healer 处理。
风险 :generator 生成的用例会真实执行点赞、关注、收藏、评论等业务操作------它不是在模拟,而是真的点。因此跑这类用例建议使用隔离测试账号。
Step 6:Heal 修复与验证
bash
对 `/tests` 中用例运行 `playwright test healer` 修复失败用例并验证修复结果。
healer 的工作流是:先跑一遍找出失败用例 → 对每条失败用例 debug 定位(看控制台日志、网络请求、页面快照)→ 做根因分析(选择器问题 / 时序问题 / 断言问题 / 应用真的坏了)→ 改代码 → 重跑验证。
最后 codex 反馈(原样):
已按 Playwright Test Healer 流程修复并验证: 修复搜索提交时序,限定搜索用例最多重试 2 次。 /tests 全量执行成功退出:9 条通过,搜索首次偶发失败后重试通过。 RESP-01 保留为预期失败,用于持续监测已知的 375px 横向溢出。
修复落地的两处代码(真实文件摘录):
ts
test.describe('搜索提交稳定性', () => {
test.describe.configure({ retries: 2 }); // 修复搜索时序:限定最多重试 2 次
test('SEARCH-01 / SEARCH-02 搜索关键词并切换结果分类', async ({ page }) => {
...
});
});
ts
test('RESP-01 三种视口下页面不发生横向溢出', async ({ page }) => {
test.fail(true, '已知站点问题:375x812 页面存在横向溢出'); // 保留为预期失败,持续监测
...
});
面对 RESP-01 这个"站点真 bug",healer 没有 硬把用例改成通过、也没有静默跳过,而是用 test.fail(true) 把"已知失败"显式固化下来------跑测试时这条用例预期会失败,一旦将来抖音修复了横向溢出,测试反而会"转绿报警",从而持续监测这个已知问题。
收尾:全量验证
到这一步,"planner 定计划 → generator 写用例 → healer 修失败"的闭环在 codex 里完整跑通了,最终产物是 10 条用例、9 通过 + 1 预期失败(持续监测)。
踩坑汇总(本版实测回顾):
- 生成测试计划是在 codex 中执行的 ------对话里要指定 profile 目录和 seed 文件,且要明确引导有头模式(抖音 headless 必被拦)
- 虽然指定了
douyin-profile,抖音仍会跳出登录并要求验证手机号后才进入登录态,需要人工介入一次 - 登录态用
saveprofile.js+storageState生成,douyin-profile/state.json含真实登录凭据 init-agents --loop要和你的平台目录对上(codex →.codex/agents/,本次没有.claude/skills);升级 Playwright 后必须重新跑一次init-agents,agent 定义文件不会自动跟随版本更新- 对"站点真 bug"(如 RESP-01 的 375px 溢出),healer 用
test.fail(true)保留为预期失败持续监测,而不是硬改或跳过 - generator 生成的用例会真实执行点赞、关注、收藏等操作,建议用隔离测试账号
4. 结论与使用建议
基于本轮实测,能下结论的有四条:
- AI 写 E2E 测试是真实可用的方向 ,机制上它读语义快照而非截图,天生比老式"截屏猜"方案稳;而且这一版验证了整个流程可以在 codex 对话里闭环------人只负责下指令和兜底登录,计划/代码/修复都是 agent 干的
- 反爬站点是最大的边界。抖音这种站,headless 一律被拦,必须 headed + 登录态,且对话里要明确引导有头模式;即便带 profile 也可能被要求验证手机号
- 断言过时和时序抖动是 AI 能帮上大忙的场景 。healer 把搜索时序问题用"限定重试 2 次"修复、把站点真 bug 用
test.fail固化为持续监测 - 别指望全自动。登录、验证码、手机号验证、滑块这些环节还是需要人工介入
✅ 适合:
-
内部业务系统、后台管理系统------页面相对稳定、无强反爬,全流程在 codex 里跑通收益明显
-
页面频繁改版、选择器频繁失效的项目------healer 自动修复断言过时,价值最直接
-
需要快速补齐测试覆盖的项目
⚠️ 慎用:
-
有强反爬、验证码、复杂人机校验的公开站点(如抖音)------需要 headed + 登录态 + 人工介入,自动化收益大打折扣
-
对测试正确性零容忍的核心支付链路------建议保留人工 review
本文作者,日常在公众号「围炉聊科技」分享前沿科技相关的技术文章,感兴趣可搜索关注。