UI 自动化:Playwright 在业务回归测试中的实践
如果只是写一两条页面脚本,Selenium、Playwright 甚至录制回放工具都能把页面点起来。真正拉开差距的,不是“能不能点”,而是当 UI 自动化进入下面这些真实场景之后,谁更容易被做成一个长期可维护系统:
- 每个版本都要跑 smoke
- 每天凌晨跑回归
- 同一套系统要覆盖多个角色
- 失败后希望不重跑也能先判断大概问题
- 后续还要进 Jenkins、进平台、进告警链路
我真正开始偏向 Playwright,不是在它“新”的时候,而是在我越来越清楚地看到:UI 自动化最贵的地方,从来不是写第一条 case,而是后面的等待、隔离、留痕、调试、失败分类和长期治理。Playwright 的价值恰好在于,它把这些高频成本尽可能提前做成了框架内能力。
一、为什么会把 Playwright 放在新项目的第一选择
判断 UI 框架时,不太看“语法是否更顺手”,而更看它会不会在半年后让项目还像一个系统,而不是一堆长脚本。
Playwright 在这方面有几个非常现实的优势。
1. 自动等待显著降低了基础噪声
现代前端页面里最常见的问题,不是元素永远不存在,而是:
- 元素短时间不可见
- 节点挂载了但还不能点
- 页面骨架先渲染,数据后渲染
- 表格列表先显示容器,再更新内容
如果框架等待太原始,后面脚本里就会迅速堆满:
sleep- 自定义轮询
- 各种临时等待 patch
脚本不是不能跑,而是会越来越不可信。Playwright 至少把这部分基础成本压低了很多。
2. Browser Context 特别适合真实权限场景
真实后台系统很少是单角色单链路,更多情况是:
- 管理员创建任务
- 审核人审批
- 普通用户查看结果
以前做这类场景,经常会被 cookie、session、localStorage 污染折腾。Playwright 的 Context 模型让多角色、多会话并行执行自然很多。
3. 失败现场留痕能力足够完整
UI 自动化最麻烦的一件事,不是失败本身,而是失败之后维护者什么都看不到。Playwright 的:
- trace
- screenshot
- video
- step 信息
对真实维护成本的影响,远比“这个 API 写起来是不是更优雅”大。
二、在项目里通常怎么搭 Playwright,而不是只写零散脚本
如果是你这种偏平台和工程化的方向,更推荐直接用 TypeScript 方案,而不是只写一些零散 JS 文件。
真实项目里,我常见的组合是:
@playwright/testTypeScriptdotenv或环境配置文件- Jenkins / GitLab CI
- HTML report + trace + video
- 测试数据工厂或接口造数脚本
原因也很直接:
@playwright/test的 runner 足够稳定- TypeScript 对页面对象、locator 和任务模型约束更好
- 后续多人协作和平台化接入更自然
三、我最反对的两种 Playwright 项目写法
1. 流水账 test 写法
test 文件里全是:
- 点击
- 输入
- 等待
- 截图
- 断言
这种写法第一条很快,后面维护成本极高。
2. “我全都用 UI 入口造数据”
看起来更像真实用户链路,但一旦进入回归体系,执行速度和失败点都会被显著放大。很多后台系统更实际的做法,是接口造数 + UI 验证展示和交互。
四、更倾向的项目目录和层次
如果是一个准备长期跑回归的 Playwright 项目,更适合拆成下面这种结构:
1 | ui-e2e/ |
这里最重要的不是目录漂亮,而是职责边界明确:
- 页面负责页面语义
- 组件负责复杂控件
- fixture 负责环境和上下文
- data 负责测试数据准备
- utils 负责公共治理能力
- tests 只表达业务意图
五、怎么设计角色隔离,而不是把所有登录态混在一起
Playwright 很强的一点,是它天然适合做多角色场景。但如果设计不好,也一样会乱。
通常会分三层来处理。
1. Storage State 层
为不同角色单独维护:
admin-state.jsonreviewer-state.jsonoperator-state.json
2. Context 层
每条业务链路里,不同角色使用不同 Browser Context,而不是在同一个上下文里来回切身份。
3. Fixture 层
通过 fixture 把环境、角色、产物保存路径、trace 策略注入进去,而不是让每个 test 自己拼。
这样做的价值是:登录态、任务上下文和失败产物天然绑定。
六、一个真实业务回归案例,怎么拆才不会越来越重
假设要覆盖“任务创建 -> 提交审核 -> 审核通过 -> 普通用户查看结果”这条后台核心链路,不适合写成一条从头点到尾的超长 E2E,而更适合按职责拆开。
场景 1:管理员创建任务
- 用管理员 storage state 打开任务页
- 点击新建任务
- 填写标题、类型、时间范围
- 提交并断成功提示
- 断言列表里出现新任务
场景 2:审核人审批任务
- 新开 reviewer context
- 进入待审核列表
- 搜索任务名
- 打开详情并审批通过
- 断言状态切换成功
场景 3:普通用户查看结果
- 新开 operator context
- 进入结果页
- 搜索任务
- 断言最终状态是“已通过”
这样拆的好处是:
- 每条脚本失败面更小
- 多角色边界更清晰
- 后续 smoke 和 regression 组合更灵活
七、测试数据怎么处理,才不会让 UI 自动化背锅
很多 UI 自动化失败,看上去是页面问题,实际上是:
- 没有可用数据
- 测试对象状态不对
- 名称冲突
- 环境里已有脏数据
所以通常会把测试数据准备显式化,而不是让 case 暗中依赖环境状态。
常见做法是两种。
1. 接口造数
在 Playwright 执行前,先调用接口准备:
- 任务
- 审批单
- 用户状态
这种方式快、稳定、适合 nightly regression。
2. UI 完整造数
更贴近用户真实路径,但速度慢、失败点多。通常只把它保留给少量高价值 smoke。
所以对很多后台系统来说,更实际的方案通常是:
接口造数 + UI 验证展示和交互 + 少量核心链路保留完整 UI 入口。
八、失败现场会保留什么,才能不靠重跑猜问题
如果这个 Playwright 项目准备长期跑,通常会要求失败时至少保存:
- 当前页面截图
- trace
- video
- 当前 URL
- 当前角色
- 最近一步操作说明
如果再深一点,还会把:
- console error
- 网络失败摘要
- 页面关键请求耗时
一起保留下来。
这样 Jenkins 一红,维护者不用立刻本地重跑,先看 trace 就能大致判断:
- 是定位失败
- 是弹窗遮挡
- 是接口慢导致页面没稳定
- 还是页面逻辑真有问题
九、Playwright 怎么接进 Jenkins 和平台,而不是只在本地跑
如果是长期项目,更适合把 Playwright 设计成天然能被两类系统消费。
1. Jenkins
负责:
- 定时任务
- smoke / regression 调度
- 构建号上下文
- 产物归档
2. UI 自动化平台
负责:
- 任务创建
- 环境、角色、浏览器管理
- 历史趋势
- 失败分类
- 告警判断
所以 Playwright 项目最好一开始就能输出结构化结果,而不只是 HTML 报告。否则后面进平台会很别扭。
十、为什么 Playwright 不是“天然稳定”,而是“更容易被做稳定”
这点非常重要。这里很容易被误解成:换成 Playwright 后脚本自然就稳了,这不现实。
它只是把几个基础问题处理得更顺手,但下面这些事情仍然需要自己补齐:
- 页面对象分层
- 稳定定位策略
- 测试数据治理
- 等待条件显式化
- 高噪声 case 清理
- 失败现场分类
否则再好的框架,也会被写成一堆“比以前现代一点,但一样脆”的脚本。
十一、怎么判断一套 Playwright 回归进入了“可维护状态”
更值得看的真实信号是:
- 新增 case 时,主要是在复用页面对象和业务动作,而不是复制点击脚本
- 多角色任务执行时,不会频繁出现登录态污染
- 失败后不用本地先复现,也能从 trace 和 screenshot 看出大概问题
- 每日 smoke 的稳定性足够高,已经能作为发布参考
- 页面变更后,改动更多集中在
pages/和components/,而不是全局搜 test 文件 - 高噪声 case 能被识别并逐步治理,而不是一直留在任务里污染结果
如果这些结果没有出现,说明 Playwright 只是“换了一个更新的语法”,还没真正进入工程化状态。
十二、结语
Playwright 在业务回归测试里的真正价值,不是把浏览器点起来,而是让 UI 自动化最昂贵的几件事更容易治理:等待、隔离、留痕、调试、多人协作和平台接入。对新项目来说,它通常是更合理的起点;但真正决定它值不值钱的,仍然是你有没有把它组织成一套长期可维护、可被团队信任的系统。