[控场AI]
· 6 分钟阅读· 3,379 字

用Claude Code写Playwright测试一个月:AI写测试,人稳结果

用Claude Code写Playwright测试一个月:AI写测试,人稳结果

日本QA总监用Claude Code写Playwright测试一个月:AI替代打字,但判断力仍靠人。

一位日本QA总监将Claude Code引入生产级Playwright E2E测试套件,经过一个月实践得出核心结论:AI能生成约70%的测试骨架代码,但无法替代QA的判断力。成功的关键在于仓库根目录的CLAUDE.md约定文件,强制使用语义选择器、禁用硬等待、保证测试隔离。AI生成的测试让作者愿意编写以前觉得不划算的边缘场景测试,其中一个挂起状态测试抓到了重复提交的真实缺陷。然而AI也有三大盲区:过度信任当前DOM、不懂什么值得测、感受不到抖动,这些都需要人工补位。最终测试编写时间减少约60%,但测试计划耗时不变,维护可控的前提是严格的选择器约定。

用Claude Code写Playwright测试一个月:AI写测试,人稳结果

一位日本QA总监用Claude Code为生产级Playwright E2E测试套件当"结对程序员",一个月后给出了一个坦诚的结论:Claude Code没有替代QA判断力,它只替代了QA敲键盘的手。

这不是玩具项目。这位作者(前SHIFT员工,日本最大QA公司出身)将AI生成的测试直接用于生产QA流水线。他在博客中详细记录了AI在测试自动化中真正擅长什么、在哪里翻车,以及最终跑通的工作流。

据其总结,核心发现有三条:Claude Code能从自然语言流程描述生成约70%的测试骨架代码;但在没有护栏的情况下,它在选择器策略和防抖动等待上表现糟糕;最佳实践是"AI写,人稳"——每个AI生成的测试必须本地跑5次、CI跑1次才算完成。

Claude Code与Playwright测试自动化实践

配置的核心:一份不可妥协的约定文件

作者的项目结构并不复杂:标准的Page Object Model目录,加上三个Playwright项目配置(桌面、移动、折叠屏)。真正的秘诀不在工具链,而在于向Claude Code提需求之前先告诉它什么。

他在仓库根目录放了一个 CLAUDE.md,里面只有三条硬规则:选择器只用 getByRole / getByLabel / [name=] 属性,绝不用CSS类链;禁止 waitForTimeout,只用Playwright自动等待和 expect() 轮询;每个测试必须能独立运行且能并行运行,不共享状态。

这三行字的威力被作者形容为"质量跃升"。没有它们,Claude Code会产出 page.locator('.css-1kxonj9 > div:nth-child(3)') 和 waitForTimeout(3000) 这类代码——正是脆弱测试套件的原材料。

这个发现对任何想把AI编码代理引入测试流程的团队都适用:AI不会自动知道你的工程约定,你必须显式地、前置地告诉它。

Page Object Model(POM) 是一种在测试自动化中广泛使用的设计模式,它将每个页面(或页面组件)封装为一个类,类中定义了该页面的元素定位器和可执行的操作方法。测试脚本通过调用 Page Object 的方法来与页面交互,而不是直接操作元素。这样做的好处是:当页面结构变化时,只需修改对应的 Page Object,而无需改动所有测试用例,从而提高了测试代码的可维护性和复用性。文章作者的项目采用标准 POM 目录结构,意味着 Claude Code 生成的测试代码也遵循这一模式,将页面元素和操作集中管理,这有助于保持测试的清晰和稳定。

语义选择器(如 getByRole、getByLabel、[name=])是 Playwright 推荐的元素定位方式,它们基于元素的语义角色、标签文本或可访问性属性来查找元素,而不是依赖易变的 CSS 类名或 DOM 结构。这类选择器与用户和辅助技术感知页面的方式一致,因此对 UI 重构有更强的抵抗力。例如,getByRole('button', { name: '提交' }) 会查找可访问名称为“提交”的按钮,即使按钮的样式或嵌套结构改变,只要语义不变,测试仍能通过。相反,使用 CSS 类链(如 .css-1kxonj9 > div:nth-child(3))则非常脆弱,类名可能被构建工具哈希化或随样式调整而改变,导致测试频繁失败。文章强调只用语义选择器,正是为了避免 AI 生成脆弱测试。

AI生成的真实测试长什么样

作者给出了一次真实交互:他要求"为联系表单写测试:成功、服务器错误、挂起状态。永远不要打真实API。"Claude Code产出了一个带网络打桩的Page Object,用 page.route 拦截 **/api/contact,支持注入状态码和人为延迟,返回模拟的成功或错误响应。

更你可能没注意到那个挂起状态测试——模拟1.5秒延迟,填写表单、提交,然后断言提交按钮被禁用、成功标题在超时内可见。作者坦言,这种测试他手工写的时候"大概不会费心去写"。

而这个测试恰好抓到了一个手动QA中反复出现的真实缺陷类别:重复提交。按钮要等React重渲染后才禁用,慢网络下两次快速点击就是两个POST请求。在联系表单上只是烦人,在结账页上就是重复扣款。

这揭示了一个反直觉的价值点:AI不只是加速你本来就会写的测试,它能让你愿意去写那些以前觉得不划算的测试,比如挂起状态、错误路径这类繁琐但高价值的场景。

AI翻车的三个地方,以及补丁

作者毫不掩饰地列出了Claude Code的失败模式,这部分对实际落地最有参考价值。

第一,它太信任当前DOM。 生成的选择器匹配的是当下存在的结构,包括自动生成的类名。补丁就是那份约定文件,加上稳定化阶段对每个选择器的人工审查。

第二,它不知道什么值得测。 你说"为仪表盘写测试",它会给你15条关于文字可见性的浅层断言。测试计划必须来自理解风险的人:支付流程、认证边界、状态转换。作者的做法是自己写计划,AI写代码。

第三,它感受不到抖动。 一次通过的AI测试不算完成。他的稳定化循环是:本地用 --repeat-each=5 跑5次,CI跑1次,审查trace看有没有静默重试。大约每10个生成的测试就有1个需要在此时返工。

这三条失败模式的共同点是:它们全都关于判断力,而非代码能力。AI能写代码,但它不知道什么值得写、什么结构是持久的、什么通过是不可信的。

测试抖动(flakiness) 是指测试在不改变代码的情况下,时而通过时而失败的现象,通常由异步操作、网络延迟、动画或资源加载时序问题引起。在 Playwright 中,抖动可能表现为元素尚未出现就执行点击,或断言在状态更新前运行。文章提到的 waitForTimeout 是一种硬等待,它强制暂停固定时间,但无法适应实际加载速度的变化,要么等待不足导致失败,要么等待过长拖慢测试。Playwright 的自动等待机制和 expect() 轮询则能动态等待条件满足,减少抖动。作者要求每个 AI 生成的测试本地运行 5 次、CI 运行 1 次,并审查 trace,正是为了识别那些偶然通过但实际不稳定的测试,确保其可靠性。

被低估的技巧:测试没人测的视口

作者配置里有一个大多数测试套件没有的项目:foldable-half-open,视口717×1172,移动端、触摸支持。

717px正好卡在Tailwind的 sm 和 md 断点之间。作者自己用摩托罗拉Razr折叠屏,发现这个缝隙里藏着的布局bug数量惊人。一个配置块的代价,换来的是高端安卓设备用户每天都会碰到的缺陷类别。

这个细节呼应了前文关于挂起状态的观察:AI降低了"边缘场景测试"的边际成本,使得那些过去因为太琐碎、太费力而被跳过的测试变得可负担。视口边界、错误路径、异步状态——这些正是生产事故的高发区。

一个月后的真实数字

作者给出的结果是克制的:测试编写时间大约减少60%,但计划阶段耗时不变,省下的是敲代码的时间;覆盖率上升,因为那些繁琐的测试不再被跳过;维护成本可控,但前提是选择器约定——他见过没有约定的AI生成套件,几周内就腐烂了。

这份记录的诚实之处在于它没有宣称"AI取代QA"。作者的原话是:Claude Code没有替代QA判断力,它替代了QA打字。测试计划、风险识别、选择器策略、抖动判定——这些依然是人来做。AI接走的是那个70%的重复劳动。

对于正在评估是否把AI编码代理引入测试流程的团队,这个案例给出了一个可复制的起点:先写约定文件,再让AI写测试,最后用多轮运行来稳定化。工具不是关键,约束才是。

#Claude Code#Playwright测试#AI测试自动化#E2E测试#QA工作流#AI编程代理#测试稳定性
分享:

相关推荐