基于Playwright的Web端到端UI自动化测试专项实践

发布时间:2026/10/18 7:43:51
基于Playwright的Web端到端UI自动化测试专项实践 如果你也是一个在 Web 项目里反复点页面的测试人应该能理解我在接手“跑者天地”时的感受。“跑者天地”是一个面向跑步爱好者的社区类 Web 平台核心功能包括跑步打卡、轨迹记录、赛事报名、社区帖子和个人数据统计。功能迭代两周一个版本每次回归都要手工点核心链路点得人发麻。这个专项的目标很明确把那些高频、重复、容易漏点的回归工作替换成一套能自动执行、自动检查、自动报障的端到端 UI 自动化测试体系。专项落地后我们搭建了基于 Playwright 的测试框架覆盖登录、跑步记录发布、赛事报名、社区互动四条核心链路累计 30 多条用例全部接入 CI 流水线稳定率稳定在 95% 以上。这篇文章记录的是整个专项从 0 到 1 的全过程包括选型时纠结过的框架、核心用例的设计思路、踩过的坑和排障细节还有一些我特别想分享的稳定性处理经验。如果你是 QA 或者测试开发又正打算给 Web 项目上 UI 自动化这篇内容应该能给你省下不少时间。1. 项目背景与自动化测试边界1.1 跑者天地Web端的核心业务链路设计一套有意义的 UI 自动化测试第一步不是打开录制工具而是先把业务链路理顺。我在专项启动前专门花了大概一个下午梳理了跑者天地 Web 端的功能地图最后把测试重点收敛到三条必须保护的核心链路上外加一个贯穿全局的数据验证场景。第一条是跑步记录链路。用户从首页发起跑步填写距离、时长、配速等信息保存后生成一条跑步记录同时个人中心的累计里程、月跑量等统计要同步更新。这是整个平台最基础也最高频的功能几乎每天都有大量用户在使用也是历次迭代中改动最多的模块优先级最高。第二条是赛事报名链路。用户从赛事列表进入详情页阅读参赛须知填写姓名、证件号、紧急联系人等信息提交报名最终在订单中心看到报名状态。这条链路涉及表单校验、名额库存、重复报名限制、订单生成等多个状态是业务上最容易出问题、也最影响资金与声誉的环节自动化价值非常高。第三条是社区互动链路。用户浏览跑步动态、发表帖子、评论点赞。它的逻辑并不复杂但涉及富文本编辑器、异步评论加载、点赞状态切换这些前端交互是 UI 自动化比较容易踩坑的地方。再加上个人中心的数据统计页也离不开前端联动我把“跑步记录发布后统计数字是否正确变化”单独抽成了一个数据校验专项用例。把这几个模块串起来就形成了专项的主干测试矩阵后续所有用例都围绕它们展开。1.2 为什么这个阶段值得做UI自动化启动专项之前项目组里有一个很现实的声音我们已经有接口测试平台了为什么还要投入做 UI 自动化这个问题值得正面回答因为它直接决定了专项能不能拿到足够的资源和耐心。接口测试覆盖的是接口层的数据逻辑它能验证“给定请求返回了正确的结果”但验证不了用户真正感知到的界面行为。举个具体例子跑步记录的保存接口返回成功但前端列表刷新逻辑如果有 bug用户看到的还是旧数据这时候接口测试是绿的页面却是错的。UI 自动化存在的意义就是补齐这最后一段用户可感知的链路把“接口成功”和“用户看到成功”之间的缝隙堵上。另一个原因是回归成本。团队两周一个版本手工回归核心链路至少花掉一个人一天半。赛事报名、跑步记录这类涉及多步骤多状态的用例手工点起来时间长漏点还非常常见。自动化落地之后每天夜间跑一遍全量早上来直接看报告该暴露的问题自动抛出来整个反馈速度是完全不一样的。投入产出比很快就显现出来了。1.3 测试范围边界做与不做UI 自动化不是万能的范围没有划清楚的项目后期一定会失控。我在立项时就把边界划得比较明确既避免了后期维护量失控也减少了扯皮。专项重点做的是核心链路的端到端验证和回归也就是上面列出的登录、跑步记录、赛事报名、社区互动、个人中心统计这几类用户可感知、跨模块、高频变动的场景。同时我也明确砍掉了一些内容。视觉像素级对比不做因为设计稿还在快速演进一个按钮改颜色的需求就能让视觉用例全线飘红维护成本大于收益。性能测试不做那是另一个专项的事情。纯接口的校验也不碰已经有现成的接口测试平台在管。图片懒加载、富文本编辑器复杂排版这类前端特有问题我采用了“只做关键断言”的方式不追求端到端还原比如发帖只校验帖子出现在列表里不校验排版是否逐像素正确。注意UI 自动化最怕的不是功能点多而是范围失控。每接一个新场景都要先问自己三个问题它是不是高频回归失败后影响有多大自动化维护成本能不能控制住三个问题里至少有两个答案是“是”才值得纳入专项范围。2. 框架选型与基础设施搭建2.1 主流框架对比与取舍工具选型上我认真对比了当前 Web 端 UI 自动化最常用的三个方案Selenium、Playwright、Cypress。说实话我在很长一段时间里是坚定的 Selenium 派毕竟它生态成熟、资料多、什么语言都能写。但这次专项我最终选了 Playwright原因是它解决了我过去几年最痛的一个点——稳定性。Selenium 的优势是兼容性和社区劣势在于它本身不提供自动等待能力。以前写 Selenium 用例最烦的就是到处加 sleep加多了用例又慢又假加少了又偶发失败。元素加载慢一点、网络抖动一下测试就跑挂了稳定性全看运气。虽然可以自己在封装层做等待工具但每个团队维护一套此类公共逻辑质量参差不齐整体效果并不理想。Cypress 对前端工程师很友好API 设计现代调试体验也不错。但它在多标签页、跨域场景的支持偏弱而且默认只能跑在自己的测试运行器里接入 CI 和扩展调试都要多做一些处理。跑者天地里存在不少第三方跳转的登录流程这类场景用 Cypress 会绕不少弯路。Playwright 最打动我的是三点内置自动等待、Trace Viewer 调试信息、原生的多浏览器支持。自动等待意味着我不需要在每条用例里写一堆 sleep框架会在元素可操作时才执行操作Trace Viewer 会记录每一次点击、输入、请求响应和页面截图排查失败时可以直接回放现场多浏览器支持正好对应跑者天地要兼容 Chrome、Firefox、Edge 的需求一套用例就能覆盖三个内核。我整理了一张选型对比表方便你有直观感受维度SeleniumPlaywrightCypress自动等待需自行封装内置内置多标签页与跨域支持支持弱多浏览器支持支持支持排查调试能力一般Trace Viewer 强较好编程语言多语言Python/Node 等Node 为主团队上手成本低但易滥用适中前端友好2.2 测试环境与数据准备UI 自动化对测试环境的依赖远高于接口测试。跑者天地早期的测试环境不太稳定数据库经常被开发同学改来改去用例一跑就是一片红根本分不清是环境问题还是代码问题。这次专项我做了两个关键决定把这类问题基本杜绝了。第一个是搭建独立的核心测试环境。这个环境不承担开发自测任务专门给自动化测试使用数据库有固定实例部署版本和线上版本保持一致。配合数据库初始化脚本每次执行前可以把数据重置到已知状态保证用例的可重复性。环境问题不是 UI 自动化专项里最难的但它是很多人最常踩的坑环境不稳定后面所有工作都是在沙滩上建楼。第二个是准备种子数据脚本。写一个脚本一次性造好测试所需的初始数据包括若干个专用测试账号、每个账号预置的跑步记录、社区帖子、赛事数据等。种子数据不要放在用例里逐条创建那样每个用例都要做前置步骤又慢又容易互相干扰。集中创建之后用例只需要依赖固定的账号 ID 和固定的数据标识简单可靠。2.3 框架目录与Page Object模型框架搭建我没有把工程做得太复杂一开始就遵循了一个适度结构化的原则方便团队成员快速接手。最终的目录结构大致如下runners_ui/ ├── config/ # 环境地址、账号信息、超时配置 ├── pages/ # Page Object 页面对象 ├── cases/ # 测试用例 ├── data/ # 测试数据与种子脚本 ├── reports/ # 报告输出 └── utils/ # 公共工具截图、重试、等待Page Object 模型是这个框架的核心。简单说就是把每个页面封装成一个类页面上的元素定位和可操作动作都定义在类里测试用例只负责按业务场景执行动作并断言结果。这样做的好处非常明显页面结构改了只需要改对应 Page 类用例本身不受影响多个用例要复用同一段操作时直接从 Page 类调用不用复制粘贴。比如跑步记录页的 Page 封装里我会暴露 start_run()、fill_metrics()、save_record() 这些方法上层用例就是一行行方法调用读起来像在描述业务步骤。这也是后面 30 多条用例能写得干净利落的关键基础。3. 核心测试场景设计与实现3.1 登录注册链路自动化登录是所有链路的入口也是自动化用例里最要命的一个模块因为验证码处理不好后面全是空谈。跑者天地的 Web 端支持手机号验证码登录、密码登录、第三方账号登录三种方式。设计上我把验证码问题拆成两级来处理测试环境关闭图形验证码短信验证码由后端测试接口直接返回固定测试码。这意味着用例里只需要在验证码输入框填入固定测试码即可不依赖真实短信通道稳定性和执行速度都有保障。前缀提醒一句这种“万能验证码”机制只能在测试环境开启上线前必须通过配置开关关闭否则就是安全漏洞。我在 config 里单独建了一个 enable_static_code 开关从根上防住误操作。登录模块我封装了 LoginPage核心方法如下class LoginPage: def __init__(self, page: Page): self.page page self.phone_input page.locator(#login_phone) self.code_input page.locator(#login_code) self.login_button page.locator(#login_submit) def login_by_phone(self, phone: str, code: str 123456): self.phone_input.fill(phone) self.code_input.fill(code) self.login_button.click() # 登录成功会跳转首页这里等待首页标识出现 self.page.wait_for_selector(.home-dashboard, timeout10000)登录模块我设计了三条核心用例。第一条是成功路径有效手机号加固定验证码登录断言跳转首页第二条是失败路径输入错误验证码断言出现“验证码错误”提示并且停留在登录页第三条是登录态校验未登录状态下直接访问个人中心 URL断言页面跳转到登录页。这三条覆盖了登录模块的主流程、异常输入和权限拦截虽然看起来简单但它们是所有后续用例的基座必须稳定。3.2 跑步记录发布与轨迹上传跑步记录模块是跑者天地最核心的功能UI 自动化要验证的不只是“能保存”还包括保存后的数据正确回显和统计联动。正常流程是这样的用户从首页进入跑步页面选择跑步类型填写距离、时长、平均配速点击保存生成记录跳转到记录详情页。自动化用例里我重点做两个断言一是记录详情中显示的距离、时长与填写值一致二是回到个人中心总里程统计增加了对应值。第二个断言尤其关键它验证了前端列表和数据统计联动的正确性这是纯接口测试覆盖不到的盲区。这里有个细节需要特别注意记录详情页会展示一张轨迹地图依赖第三方地图渲染引擎地图素材加载往往比普通 DOM 元素慢。如果只判断“地图容器出现了”可能容器是空的canvas 还没画出来。我的做法是等待地图组件内部暴露的“渲染完成”标记比如一个只有在异步加载结束后才会出现的子节点或者地图实例挂载后写入的特定 class。写过这类场景的人都懂这个等待点找对了用例稳定性会提升一大截。再补充一个 GPX 轨迹上传的用例。用户可以从文件上传入口提交一份 GPX 轨迹文件系统解析后在地图上绘制轨迹。测试时要准备标准 GPX 文件和异常 GPX 文件。标准文件上传后断言轨迹点数展示正常、地图进入轨迹展示状态异常文件上传后断言页面出现“文件格式不支持”的提示。GPX 文件用最小有效集合就够了我之前见过有人为了“尽量真实”传了十几 MB 的文件结果用例跑一次要几十秒完全没有必要。3.3 赛事报名全流程赛事报名是跑者天地里业务支撑价值最高的模块流程本身长状态又多是非常适合自动化的场景。完整流程是从赛事列表页选择一个赛事进入详情页阅读参赛须知并勾选同意填写姓名、身份证号、紧急联系人等信息提交报名跳转到报名成功页。自动化用例设计上我关心三个状态报名成功主路径、名额已满的抑制路径、已报名用户的重复报名提示。成功主路径要覆盖表单必填项校验不填姓名直接提交应该出现“请输入姓名”的提示填对全部信息则提交成功跳转到成功页并在订单中心出现对应的报名记录。这属于验证前端表单校验与订单生成的联动。满员状态在真实环境很难稳定模拟因为名额是随时变化的。我在种子数据里预置了一个名额为 0 的赛事用例打开这个赛事详情页直接断言报名按钮为禁用状态且展示“名额已满”。这种思路摆脱了对动态业务数据的依赖稳定且好维护。重复报名场景则是预置一个已经报名过某赛事的测试账号再次进入报名页时断言页面出现“您已报名该赛事”的提示报名按钮不可点击。这类状态用真实业务数据很难构造但通过种子数据可以任意堆叠这也是为什么我反复强调种子数据脚本是基础设施。3.4 社区互动与个人中心统计社区互动功能逻辑上不算复杂但它影响平台用户留存使用频率也很高。我的自动化覆盖了发帖、回帖、点赞三件事。发帖用例关注富文本编辑器填入标题和内容点击发布断言新帖子出现在列表最前面。回帖用例验证评论输入框提交后评论区出现新评论且评论数从 0 变成 1。点赞用例关注点赞后的按钮状态变化从空心到实心点赞数加 1。这三个动作都是用户每天高频使用的功能任何一次迭代改动都可能影响它们放在回归集里非常有必要。个人中心统计页我设计了一个“数据联动校验”的用例先用种子数据构造一个测试账号该账号预置 5 条跑步记录总里程固定然后执行一遍跑步记录发布用例新增一条记录再回到个人中心断言总里程等于预置里程加新增里程。这个用例把跑步记录发布和个人统计串成完整链路一旦统计逻辑出错自动化就能在第一时间暴露。相比反复校验单个页面这种跨页面联动断言的价值更高也更接近用户真实的使用路径。此外空数据状态一定要测。新注册账号没有任何跑步记录时个人统计页应该展示引导文案“还没有跑步记录去开始你的第一跑吧”而不是一片空白。空状态是前端最容易漏测的场景很多实际 bug 都是在没有数据时出现的自动化覆盖这个点投入产出比极高。4. 执行调度与稳定性加固4.1 用例分级与CI调度用例建起来之后执行策略决定了它的使用体验。我把用例分为两级。第一级是冒烟集对应登录、跑步记录发布、赛事报名成功、发帖这四条主链路每次版本发布前必须在 CI 流水线上强制通过。这部分用例控制在 10 条以内执行时间控制在 5 分钟以内追求的是第一遍必须全绿。冒烟集是开发提交代码后最快的一道自动防线跑不通就直接打回不让问题流到测试环节。第二级是全量回归集包含全部 30 多条用例覆盖异常路径、数据联动、空状态等细节场景。全量集每天夜间定时执行一次第二天早上团队直接看报告。夜间执行的好处是执行时间不受研发联调影响环境也相对安静。我在 CI 流水线上加了触发规则定时任务跑全量标签触发跑冒烟集。开发在提交代码时也可以手动触发冒烟集这样核心流程在合并前就能先跑一遍。4.2 等待策略告别脆弱sleepUI 自动化稳定性最关键的一点是等待策略没有之一。Playwright 内置了动作等待click()、fill() 这些方法会自动等元素可见、可交互之后再进行操作这已经解决了 70% 的问题。但业务层面有些等待仍需自己定义比如“等待列表数量从 5 变到 6”“等待统计数字更新为指定值”这些属于业务状态的等待。我封装了一个断言等待器用于等待某个 UI 状态达到预期def wait_until(page, expect_func, timeout10000, interval500): end_time time.time() timeout / 1000 while time.time() end_time: try: if expect_func(): return True except Exception: pass time.sleep(interval / 1000) return False这种条件等待方式配合显式断言能做到不早不晚稳定又不会白白浪费时间。我不建议在用例里写死 sleep(5)因为不同机器、不同网络环境下同样的 5 秒含义完全不同。条件等待是依据实际页面状态判断的网络慢就多等执行快就少等这才是合理的等待逻辑。4.3 失败处理与可观测性再完善的用例也有跑挂的时候关键是挂完之后你能否在几分钟内定位问题。我的做法是三重可观测性。第一重是失败截图。每个用例失败时自动捕获全屏截图保存到报告目录。截图时机要控制在断言失败的那一刻因为这时页面还停留在现场能看到真实问题所在。第二重是录制视频。Playwright 可以给整个会话录制视频失败后把对应视频片段一起归档很多时序和动画问题通过回放看得非常清楚。第三重是 Trace 文件。这是 Playwright 特有的调试信息记录完整的操作步骤、网络请求和响应配合它自带的调试工具可以逐帧查看整个用例的执行过程。三层信息都会通过告警通知随报告一起推送给团队成员。在重试机制上我在用例级配置了失败重试 2 次。这里有一个关键提醒重试必须是幂等的。像报名、发帖这类会产生数据的用例重试前要保证数据源被重置否则第二次执行会因为“已报名”或者“帖子已存在”而失败重试反而帮了倒忙。对于写操作用例我通常会给它们单独配一套专用账号每次执行后用脚本清理数据确保重试环境干净。5. 专项推进中的典型问题与排障实录5.1 元素定位到了却被遮挡弹窗与消息提示干扰专项第一批用例刚接入 CI 时稳定性一度只有 70% 左右。最典型的假失败现象是元素明明定位到了点击却报超时。排查几次后发现罪魁祸首是页面上的消息提示和弹窗浮层。跑者天地保存跑步记录后右上角会弹出一个轻提示。提示出现的时候它恰好覆盖了下一步要点击的“完成”按钮用例傻傻地去点击就一直点不到直到超时。解决方案有两个一是点击前先滚动到元素确保它在可视区二是等待遮挡浮层消失再操作。我在 Page 封装里统一加了一个 dismiss_floating_layer 方法凡是页面有弹窗类遮罩的操作前先调用一次稳定性立刻有了明显提升。5.2 全量回归大面积变红环境数据被重置有一次全量回归早上跑完报告里 20 多条用例全红。第一反应是产品提了代码但开发确认前一夜没有代码改动。逐个排查后发现是测试环境数据库在夜里被重建了种子数据全没了测试账号也不存在。登录用例本身能过但后续所有依赖这些账号的用例全部失败。这件事让我意识到一个重要问题执行前必须做环境健康检查而不是等失败报告堆了一大堆才去排查环境。我在 CI 前置阶段加了一个 check_environment 脚本先检查测试账号是否存在、种子赛事数据是否存在、核心页面是否可访问任何一个检查不通过就直接中止执行并告警。环境健康检查是对抗环境问题的第一道防线强烈建议每个 UI 自动化项目都加这一层。5.3 地图组件加载缓慢导致的误报专项跑了一段时间之后我发现失败率最高的模块居然是跑步记录详情的地图渲染而且失败还很不稳定间歇性发作。一开始我以为地图相关功能有 bug后来反复对比发现问题出在我的断言时机上。地图引擎依赖网络加载慢的时候要十几秒。我最初的判断条件是寻找地图容器 div 是否可见但后来发现容器 div 在初始化时就存在了地图实际还没画完断言自然就会偶发地过早失败。之后我把判断条件改成了等待地图引擎在容器内插入的特定子节点这个节点出现才代表渲染完成同时把超时时间从默认 5 秒加长到 15 秒。这里分享一个通用经验第三方组件渲染完成的标志不要通过“元素存在”来判断而要找一个只有渲染完成后才会有的状态比如特定 class、特定子结构或者某个数据属性。5.4 报表与通知让自动化结果真正被看到自动化跑完结果如果只躺在报告里价值就打了一半折扣。我在 CI 流水线末端做了两件事第一生成 HTML 格式测试报告包含每个用例的执行状态、耗时、失败截图和 Trace 链接第二通过团队 IM 机器人把结果推送到群里内容包括通过率、失败用例列表和失败原因摘要。推送消息里的失败原因摘要很有用它会把断言失败时的期望值和实际值直接打出来成员不用点进报告就知道大概问题出在哪。我还有一个工作习惯每周统计同一条用例重试后的失败次数如果某条用例连续多日重试后仍然失败基本可以判定是真实缺陷或者用例设计有误必须当天处理不能任由它一直挂着。专项落地跑了一个月后跑者天地的 UI 自动化稳定率稳定在 95% 以上冒烟集每次发版前都跑全量集每天夜里跑。我个人最大的体会是UI 自动化的核心不在于能写出多少个用例而在于能不能把最核心的业务链路保护得稳稳当当。宁可先把 10 条冒烟用例打磨到极其稳定也不要一口气堆 100 条三天两头红一地的用例。另外重试后仍然失败的那几条用例你要当成宝贝一样去排查它们往往就是真实缺陷还没有修复的征兆。最后再分享一个小技巧跨浏览器巡检这类慢任务放到夜间全量集里跑日常发布不要被它拖着走团队体验会好很多。希望这篇专项报告能帮你少走一点我曾经走过的弯路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询