关于 playwright-testing
面向前端开发者的测试技能,帮助选择合适的测试层级,并通过可观测性和确定性降低误报与不稳定。涵盖 Playwright MCP 的端到端测试流程,也适用于 Canvas 游戏。
主要功能
- 01按风险选择测试层级
- 02控制时间、随机性与网络
- 03添加稳定的应用测试接口
- 04收集错误、请求和截图证据
- 05对截图进行视觉差异比较
适用场景
- 01为关键用户流程编写端到端测试
- 02稳定 Canvas 游戏测试
- 03诊断 CI 中的间歇性失败
MCP 相关
提供使用 Playwright MCP 执行浏览器导航、页面检查、控制台与网络诊断及截图的工作流。面向 Playwright MCP 工具的客户端使用者。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ chongdashu/ phaserjs-oakwoods --skill playwright-testing安装后在 Claude Code 中输入/playwright-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/flake-reduction.mdphaser-canvas-testing.mdplaywright-mcp-cheatsheet.mdscripts/imgdiff.py详细介绍
前端测试
快速建立可靠信心:选择合适的测试层、让应用具备可观测性,并消除不确定性,从而安全地重构,并让失败具有可操作性。
理念:每分钟获得的信心
前端测试失败有两个原因:产品出了问题,或者测试在撒谎。你的任务是最大限度地提高信号质量,尽量避免“测试在撒谎”。
编写测试前,先问自己:
- 我在防范哪种用户风险(金钱、进度、身份验证、数据丢失、崩溃)?
- 哪个范围最小的测试层可以捕获这类 bug(纯逻辑、UI,还是完整浏览器)?
- 存在哪些不确定性(时间、随机数生成器、异步加载、网络、动画、字体、GPU)?
- 除了
setTimeout,我还能等待什么“就绪”信号? - 失败时应该打印或截图哪些内容,才能在 CI 中诊断问题?
核心原则:
- 测试契约,而非实现:断言稳定且对用户有意义的结果,以及公开的接口。
- 确定性优先于重试:让时间、随机数生成器和网络可控;从源头消除不稳定因素。
- 像调试器一样观察:发生失败时,记录控制台错误、网络故障、截图和状态转储。
- 先覆盖一个关键流程:可靠的冒烟测试胜过 50 个不稳定的测试。
测试层级决策树
选择能提供所需信心的最轻量级测试层:
| 层级 | 速度 | 用途 |
|---|---|---|
| 单元测试 | 最快 | 纯函数、reducers、验证器、数学计算、寻路、确定性模拟 |
| 组件测试 | 中等 | 使用模拟 IO 的 UI 行为(React Testing Library、Vue Testing Library) |
| E2E | 最慢 | 覆盖路由、存储和真实打包及运行时环境的关键用户流程 |
| 视觉测试 | 专用 | 布局或像素回归;对于 canvas/WebGL,仅在锁定确定性之后进行 |
快速开始:编写第一个冒烟测试
- 定义 1 个关键流程:“页面加载 → 用户可以开始 → 一个关键操作正常”
- 在应用中添加测试接口(见下文)
- 选择测试运行器:E2E 使用 Playwright MCP,逻辑使用单元测试
- 明确暴露失败:将控制台错误和请求失败视为测试失败
- 确保稳定:设置随机数种子、冻结时间、固定视口、禁用动画
具体 MCP 工作流:测试游戏
测试 Phaser/canvas 游戏的分步流程:
1. mcp__playwright__browser_navigate
→ http://localhost:3000?test=1&seed=42
2. mcp__playwright__browser_evaluate
→ () => new Promise(r => { const c = () => window.__TEST__?.ready ? r(true) : setTimeout(c, 100); c(); })
(Wait for game ready)
3. mcp__playwright__browser_console_messages
→ level: "error"
(Fail if any errors)
4. mcp__playwright__browser_snapshot
→ Get UI state and refs
5. mcp__playwright__browser_click
→ element: "Start Button", ref: [from snapshot]
6. mcp__playwright__browser_evaluate
→ () => window.__TEST__.state()
(Assert game state is correct)
7. mcp__playwright__browser_press_key
→ key: "ArrowRight" (or WASD for movement)
8. mcp__playwright__browser_evaluate
→ () => window.__TEST__.state().player.x
(Verify movement happened)
9. mcp__playwright__browser_take_screenshot
→ filename: "gameplay-state.png"
(Visual evidence after deterministic setup)
建议添加的测试接口
为便于测试,可在应用中添加以下内容(只读、稳定、精简):
window.__TEST__ = {
ready: false, // true after first interactive frame
seed: null, // current RNG seed
sceneKey: null, // current scene/route
state: () => ({ // JSON-serializable snapshot
scene: this.sceneKey,
player: { x, y, hp },
score: gameState.score,
entities: entities.map(e => ({ id: e.id, type: e.type, x: e.x, y: e.y }))
}),
commands: { // optional mutation commands
reset: () => {},
seed: (n) => {},
skipIntro: () => {}
}
};
规则:公开 ID 和必要字段,不要公开原始 Phaser/引擎对象。
应避免的反模式
❌ 选错测试层:为纯逻辑编写 E2E 测试
看似合理的原因:“干脆通过浏览器测试所有内容吧。”
更好的做法:逻辑使用单元测试;E2E 留给集成契约
❌ 测试实现细节:断言 DOM 结构或类名
看似合理的原因:通过 DevTools 可以轻松断言看到的内容
更好的做法:断言对用户有意义的结果(文本、分数、HP 变化)
❌ 依赖休眠的测试:wait 2s then click
看似合理的原因:简单,而且“在我的机器上能跑”
更好的做法:等待明确的就绪信号(DOM 标记、window.__TEST__.ready)
❌ 随机性不受控:在断言中使用随机数生成器或时间
看似合理的原因:“游戏使用随机数,测试也应该如此。”
更好的做法:设置随机数种子(?seed=42)、冻结时间,并断言稳定的不变量
❌ 未确保确定性就进行像素快照:canvas 截图不稳定
看似合理的原因:“这样可以自动捕获视觉 bug。”
更好的做法:先启用确定性模式,再在已知稳定的帧截图
❌ 把重试当作策略:“把重试次数加到 3 就行。”
看似合理的原因:快速修复,让 CI 通过
更好的做法:修复导致不稳定的根源;重试会掩盖真正的问题
调试失败的测试
测试失败时,按以下顺序收集证据:
- 控制台错误:
mcp__playwright__browser_console_messages({ level: "error" }) - 网络故障:
mcp__playwright__browser_network_requests()→ 检查是否有非 2xx 状态码 - 屏幕截图:
mcp__playwright__browser_take_screenshot()→ 查看失败时的视觉状态 - 应用状态:
mcp__playwright__browser_evaluate({ function: "() => window.__TEST__.state()" }) - 分类间歇性故障(参见 references/flake-reduction.md):
- 就绪状态?→ 添加显式等待
- 时序问题?→ 控制动画/物理效果
- 环境问题?→ 固定视口/DPR
- 数据问题?→ 隔离测试数据
达标标准:测试做到什么程度才算“足够”?
最低可行测试套件:
- 1 个冒烟测试,证明应用已加载且主要操作可用
- 存在测试接缝(
window.__TEST__,包含就绪标志和状态) - 确定性模式(适用于 canvas/游戏;
?test=1启用种子设定) - 控制台错误会导致测试失败(不允许静默失败)
- CI 在每次推送时运行测试
满足以下条件时可进一步完善:
- 关键路径(身份验证、付款、保存/加载)有专门的 E2E 测试
- 单元测试覆盖复杂逻辑(寻路、伤害计算、状态机)
- 对关键屏幕(菜单、HUD)进行视觉回归测试,并锁定确定性
使用 imgdiff.py 进行视觉回归测试
比较屏幕截图的像素:
# Compare baseline to current
python scripts/imgdiff.py baseline.png current.png --out diff.png
# Allow small tolerance (anti-aliasing differences)
python scripts/imgdiff.py baseline.png current.png --max-rms 2.0
退出代码:0 = 相同,1 = 不同,2 = 错误
UI 切片回归(九宫格切片 / 丝带 / 条)
Canvas UI 问题(面板接缝、分段丝带、不可见的 HUD 填充)最好通过专门的 UI 测试工具来捕获,而不是依赖完整的游戏流程。
- 构建简单的
test.html/场景,只加载 UI 资源。 - 将原始切片与组装后的面板并排渲染(多种尺寸),并展示采用“原始裁剪 + 缩放”和“多切片拼接”两种方式的丝带/条。
- 暴露
window.__TEST__,并提供.commands.showTest(n),以便 Playwright 确定性地切换各模式。 - 截取指定区域的屏幕截图(面板、丝带、条),并在 CI 中进行差异比较。
确定性设置和屏幕截图工作流请参见 references/phaser-canvas-testing.md。
按场景调整指南
根据上下文调整方法:
- DOM 应用:使用标准 Playwright 选择器,等待文本/元素
- Canvas 游戏:必须设置测试接缝,通过
window.__TEST__.ready等待 - 混合应用:菜单使用 DOM,游戏过程使用测试接缝
- 仅 CI 环境有 GPU:可能需要软件渲染标志,或跳过视觉测试
- UI 切片回归:对于九宫格切片/丝带/条的瑕疵,优先使用小型 UI 测试场景/页面,配合确定性模式和指定区域的屏幕截图(
references/phaser-canvas-testing.md)。
随附资源
需要时请阅读:
references/playwright-mcp-cheatsheet.md:MCP 工具模式详解references/phaser-canvas-testing.md:Phaser 游戏的确定性模式references/flake-reduction.md:间歇性故障的分类和修复方法
请记住
只需添加一个小而稳定的接缝,用于检查就绪状态和状态信息,几乎任何前端(包括 canvas/WebGL 游戏)都能变得可测试。一个可靠的冒烟测试就是基础。目标是让测试易于维护:结果确定、明确检查就绪状态,并能提供丰富的失败证据。目标是建立信心,而不是追求覆盖率数字。