ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Playwright Trace录制详解:从配置到排错实战

Playwright Trace录制详解:从配置到排错实战 1. Trace 到底录了什么 —— 为什么它比截图和录屏更适合排错做自动化测试这几年我踩过最多的坑不是脚本写不出来而是用例挂了以后不知道它到底为什么挂。尤其是 CI 上的偶发失败本地重跑八遍全绿一到流水线就红。以前我靠截图和 console 日志硬猜后来接触到 Playwright 的 Trace 录制才算是把盲人摸象变成了全息回放。Trace 是 Playwright 内置的一种执行记录机制它会把测试运行期间的每一步操作、每个操作前后的页面状态、每次网络请求、每一条 console 日志、包括执行到的源码位置全部打包进一个 zip 文件里。跑完用例之后你可以在浏览器里把这个 zip 打开像看录像一样逐步回放整个执行过程而且每一步都能点开查看当时的真实页面状态。这篇文章我会把 Playwright 添加 Trace 录制的几种方法、参数细节、查看技巧和实际踩过的坑完整过一遍适合正在用 Playwright 做 Web 自动化、又苦于排查不稳定用例的测试开发同学。1.1 Trace 的三层信息行为、状态、日志先别急着上代码搞清楚 Trace 文件里装的是什么后面配置起来才不会盲目。我习惯把 Trace 的信息分成三层来看。第一层是行为层。Playwright 会把测试脚本里的每一步动作goto、click、fill、waitForSelector、expect 等按时间顺序记录下来形成一个 Action 列表每条 action 都带时间戳、耗时和参数。比如 click 的时候鼠标点在了哪个坐标、fill 填了什么文本这些都会留下记录。这个列表的价值在于你能精确知道用例在时间轴上的哪一步开始出问题而不是只看到最终失败的那一下。第二层是状态层。在每一步动作之前和之后Playwright 会保存当前页面的 DOM 快照。这一点和截图有本质区别截图是一张位图你只能看到像素DOM 快照是结构化数据可以在 Trace Viewer 里选中任意一个元素直接查看它的 HTML 结构、class、id、文本内容。比如页面底部有个弹层把按钮盖住了截图里你只能隐约看到好像有东西挡着DOM 快照能让你直接把那个遮挡元素的代码抠出来甚至看清它的层级关系。第三层是日志层。包括网络请求列表URL、方法、状态码、请求头、响应头、响应体、console 消息log、warn、error、页面报错pageerror、以及执行到的源码位置。很多用例挂掉的原因不在交互动作本身而是某个 xhr 接口超时了、某个静态资源 404 了这类问题在 Trace 里一眼就能看到。我见过最典型的一个场景是断言一直失败截图看起来按钮就在那点开网络才发现按钮依赖的接口在 CI 环境返回了 500页面其实渲染的是一个错误占位结构只是长得很像正常按钮。1.2 和截图、录屏的对比我整理了一个对比表格方便你直观感受三者的差异维度截图录屏Trace记录时机手动/失败时全程全程记录内容单帧画面连续画面操作DOM快照网络日志源码能否检查元素结构否否能能否看到网络请求否否能文件体积小大中可控回放精度无视觉级状态级录屏适合给非技术人员看现场发生了什么Trace 适合你自己去定位为什么发生。如果你和我一样需要在 CI 上处理偶发失败Trace 几乎是无脑首选。它的体积不比视频大但信息密度高出一个数量级。1.3 什么样的用例最值得开 Trace不是所有用例都需要开 Trace。我的经验是这三类最值得CI 上偶发失败的用例。本地复现不了只能在流水线里留证据。强依赖接口返回和时序的用例。比如页面要等某个接口数据渲染接口稍慢就挂。涉及第三方登录、支付、弹窗这类交互频繁的场景前后状态对比特别有用。跑得又稳又快的纯前端展示类用例开不开都行开了也只是增加一点执行开销和 artifact 存储。2. 三种添加 Trace 录制的方法命令行、配置文件、代码控制Playwright 给 Trace 提供了多种打开方式我按使用频率从高到低来讲。2.1 命令行参数最快速的临时验证如果你只是想在本地复现一个疑难用例最简单的方法就是给测试命令加上参数。以 Playwright Test 为例npx playwright test my-test.spec.ts --trace on这一句会在跑完测试后把所有用例的 trace zip 保存到test-results/trace/目录下。命令行支持的值和配置文件里一致on、off、retain-on-failure、on-first-retry。它的优先级高于配置文件所以临时想看某个用例的时候不用改配置直接命令行覆盖就行跑完再决定要不要落在配置里。这个特性我几乎每周都会用尤其是怀疑某个用例有偶发问题的时候先带--trace on跑两三遍大概率能抓到现场。2.2 配置文件让每个用例都带上录制长久的方案是写进playwright.config.ts的use块import { defineConfig } from playwright/test; export default defineConfig({ use: { trace: on-first-retry, }, });四个枚举值的含义要弄清楚它们不是同一件事off不录制默认值。on每次跑都录不管通过还是失败。retain-on-failure用例通过时不保存失败时保存。注意它的字面意思——保留失败的 trace它还是会全程录制只是只在失败时才把内容落盘。on-first-retry只在第一次重试时录制。如果用例第一次就挂了但没配重试它不会有任何 trace。这个值通常和retry配合用才有意义。我建议的组合是retry: 2trace: on-first-retry既能拿到失败现场的完整回放又不会在每次全量回归时生成一大堆文件。这个组合背后有一个现实考量稳定的用例开on纯粹是浪费磁盘和后续清理时间CI 存储也是成本。2.3 代码控制手动管理录制生命周期有些场景你不想给整个项目开 trace只想单独录一段关键操作。这时候可以用tracingAPItest(手动录制一段关键流程, async ({ browser }) { const context await browser.newContext(); await context.tracing.start({ screenshots: true, snapshots: true }); const page await context.newPage(); await page.goto(https://example.com); await page.click(button#submit); await context.tracing.stop({ path: test-results/manual-trace.zip }); });tracing.start()支持screenshots和snapshots参数允许你只录快照不录截图或者反过来。手动模式的好处是精细坏处是容易忘掉stop——一旦漏调zip 不会落盘Trace 就白录了。我一般建议用try/finally包住 stop或者干脆优先用配置文件方案把生命周期交给框架管理。如果你用的是 Playwright Test 而不是裸的 Playwright 库框架本身就会在用例结束时自动收尾不需要手动调stop只有browser.newContext()这种脱离测试框架的写法才需要你手动管理。还有一个容易被忽略的姿势在测试中通过testInfo.attach()把手动录制的 trace 挂到 HTML 报告里。这样你打开npx playwright show-report时可以直接在用例详情页里看到 trace 链接不用自己跑去 artifacts 目录翻文件。需要说明的是配置了trace之后HTML 报告会自动内嵌 Playwright 框架生成的 trace 查看入口testInfo.attach主要用于手动录制场景或自定义报告的需求。3. 录制参数的取舍快照、截图和源码各控制什么Trace 本身也支持细粒度控制。很多人以为开了trace: on就只能全量录制其实不是。在配置里可以传对象use: { trace: { screenshots: on, snapshots: on, sources: on, }, }trace选项既可以是枚举字符串也可以是配置对象对象里三个字段分别控制三类内容。搞清楚每个字段的作用你才能在信息完整和体积可控之间找到平衡点。3.1 screenshots / snapshots / sources 三个开关的真实作用screenshots是否录制每个动作的截图。可选off、on、only-on-failure。截图能让你在回放时看到页面长什么样但很占体积。如果你的主要需求是看 DOM 快照和网络日志可以把截图关掉。我个人常用的折中是only-on-failure平时不占空间失败时能拿到视觉证据。snapshots是否录制 DOM 快照。这是 Trace 最值钱的部分默认开建议不要关。没有快照Trace Viewer 基本就退化成一个带网络日志的操作列表排查能力大打折扣。sources是否记录执行时的源码片段。会在 Trace Viewer 的 Source 标签里显示脚本命中的源码位置方便你快速确认当前执行到哪个文件的哪一行。这个信息量不大体积增加也有限一般保持默认开。这里有个容易误解的点trace配置对象和枚举字符串不能混用比如你不能写trace: on然后又想单独调 snapshots这时需要把整个对象写全。Playwright 会覆盖同字段而不是合并两边。3.2 控制 Trace 体积的三种务实手段体积问题在本地不痛不痒到 CI 上就是实打实的存储和下载成本。实测下来一个 20 步左右的用例全量开on的 zip 大概 38 MB如果关掉截图只留快照能压到 1 MB 以内。回归用例动辄几百上千条这个差异会直接影响流水线的 artifact 限额和查看 trace 时的加载速度。控制体积的思路有三个能不开就不开。稳定用例保持off只对重跑或失败保留 trace。精确控制内容。screenshots: only-on-failure是个好折中网络日志和 DOM 快照本身并不算特别占空间。设置 CI 的 artifact 保留期。GitHub Actions 之类的流水线可以给 trace 目录设置只保留最近 N 天避免长期堆积。如果用例里还有循环遍历 大量接口访问单一用例的 trace 很容易膨胀到几十 MB这种时候更要靠上面的手段兜底。我见过有人为了排查一个问题全量开 trace 跑了一晚上结果 CI 直接因为 artifact 超限挂掉反而把真正的问题淹没了。4. 打开 Trace 的实操姿势Trace Viewer 里的高效排错路径光会录不会看等于白录。这一段我讲我怎么在 Trace Viewer 里快速定位问题这套路径我用了大半年基本能覆盖九成以上的偶发失败场景。4.1 两扇大门命令行打开和 HTML 报告内嵌录制完成后trace zip 一般落在test-results/下。在本地打开npx playwright show-trace test-results/trace/user-login-retry.zip也可以直接打开 HTML 报告npx playwright show-report在报告里找到对应用例点击 trace 链接会弹出独立的 Trace Viewer 页面。CI 上的用法是把test-results/目录作为 artifact 上传在流水线构建记录里下载 zip然后用show-trace在本地打开。这里有个细节Trace Viewer 是纯前端的zip 文件用本地路径即可打开不需要部署任何服务甚至断网也能看这在出问题现场的时候尤其好用。4.2 按图索骥从 Action 时间线到问题根因打开 Trace Viewer 后左边是 Action 列表右边是页面快照区底部有 Network、Console、Source、Errors 几个标签页。很多新手一上来就乱点我的习惯是按以下顺序第一步看哪步红了。Trace Viewer 会在失败动作上标红先点那个动作看 page 报了什么错堆栈指向哪一行。这一步能快速确认是断言失败还是元素查找失败。第二步对比 before/after。每个动作都有前后两个快照用键盘左右键可以切换。最常见的场景是点击按钮没反应前后快照一对比你立刻能看出页面上是不是多了个遮罩层、弹窗、或者某个元素消失了。这个能力是 Trace 相对截图的碾压性优势——截图只能看现在Trace 能看刚才和现在。第三步查网络。如果失败和接口有关切到 Network 标签按状态码排序500、404、超时的请求一眼就能挑出来。Trace 里网络记录不是只给你一个 URL它连请求的发起时间、耗时、请求体和响应体都提供了配合 action 时间线你甚至能算出接口返回 800ms但代码只等了 300ms这种竞态问题。第四步查 console 和 pageerror。前端 JS 抛异常往往不会直接导致断言失败但会改变后续行为。比如某个页面初始化脚本报了错导致后续按钮事件绑不上这种问题只有看 console 才能发现。Errors 标签页会汇总所有未捕获异常优先看它。4.3 一个具体例子偶发 element not found 的标准排查链路拿偶发 element not found举例标准排查路径是这样的在 Trace Viewer 里找到查找该元素的 action看它标红的前一步是哪个。切到 before 快照检查该元素此时的 DOM 是否存在如果存在再看它的位置是否被其他元素遮挡视口里是否真的可见。切到 Network看该元素依赖的接口在失败时刻是否返回了非 200或者响应时间是否异常。切到 Console看有没有未捕获异常或者资源加载失败。这样一轮下来绝大多数偶发问题都能归因到具体环节要么是接口时序要么是前端渲染条件不满足要么是弹窗遮挡而不是以前那样对着截图瞎猜。把排查过程固化成这套顺序之后我处理同类问题的平均耗时从一两个小时降到了十几分钟。5. 实测中踩过的坑Trace 不生效和不完整的常见原因这部分是纯经验全都来自我在真实项目里摔过的跟头。配置文档写得很清楚但现实环境总是会多出一些文档里没写的情况。5.1 配置了 trace 但目录里没有 zip最常见的原因是值和预期不符。on-first-retry只有在实际发生重试时才会录制用例一次通过就没文件非常容易让人误以为配置失效。所以我调试时总是先用--trace on强制验证一遍排除配置问题后再回到正式配置。另一个坑是项目里存在多个playwright.config或者分 project 配置。Playwright 的配置合并规则是先 project 后全局你如果在一个 project 里覆盖了use.trace而全局文件里配置的是另一个值最终以 project 里的为准。排查时直接看实际执行时用的哪个配置文件别只改全局那一份。我遇到过同事改了半天全局配置没反应最后发现是某个 spec 文件顶部用test.use()单独把 trace 关掉了。5.2 并列 worker 的 artifact 丢失默认情况下 Playwright 用多 worker 并行跑测试每个 worker 的 trace 会写到各自独立的路径里。如果你在 CI 上用了类似只上传test-results/trace/*.zip的通配符通常没问题但如果你把 trace 目录自定义成了所有用例共享的单一输出路径多个 worker 同时写同一个目录就会出现文件互相覆盖或写一半的情况。我的建议是保持默认的outputDir行为不要自己定义一个所有 worker 共用的输出路径省得在 CI 上出现trace 文件存在但打开是坏的这类诡异问题。5.3 录了 trace 但 Viewer 打开是空白这个坑一般出在版本不一致上。Trace zip 的格式是跟随 Playwright 版本演进的用不同版本的 CLI 打开旧版本生成的 zip偶尔会出现加载不出来或者只有部分数据。对应办法是打开 trace 时尽量用触发录制时相同版本的playwright/test。CI 上打包的 node_modules 版本和本地不一致是最常见的诱因建议 CI 和本地都锁同一个版本别用latest这种浮动的标签。5.4 体积爆炸导致 CI 超时trace 全量开启后如果用例里还有循环遍历加大量接口访问zip 很容易膨胀到几十 MB上传 artifact 的时间和存储成本都会上来。我遇到过一晚上跑完 CI 直接触达 artifact 限制的情况。针对这种场景前面说的screenshots: only-on-failure就是救命的另外配合retry: 2 on-first-retry能把体积控制在完全可接受的范围。还有一个容易忽略的细节手动调用context.tracing.stop({ path })时如果不传path文件不会保存。如果手动录制过程中调了好几次start/stop中间没落盘的部分其实会被回收但你也就拿不到数据了。所以手动录制务必确认 stop 时传了 path最好用绝对路径避免工作目录切换导致文件不知道跑哪去了。6. 我最终在 CI 上落地的配置与决策逻辑最后给出一份我目前在实际项目中稳定跑着的配置供参考。它不是最复杂的却是踩完所有坑之后沉淀下来最省心的。import { defineConfig } from playwright/test; export default defineConfig({ retries: process.env.CI ? 2 : 0, reporter: [ [list], [html, { open: never, outputFolder: playwright-report }], ], outputDir: test-results, use: { trace: process.env.DEBUG_TRACE ? on : on-first-retry, screenshot: only-on-failure, video: off, }, });6.1 这份配置的每一行是怎么来的retries: process.env.CI ? 2 : 0的意思是本地跑不重试CI 上允许每个用例最多重试两次。这解决的是偶发失败的统计问题很多情况下用例第一次挂是环境抖动重试一次就过了但我们需要的是重试时的现场记录。trace: process.env.DEBUG_TRACE ? on : on-first-retry是这套配置的核心。日常回归走on-first-retry只有重试发生时才生成 trace当某个用例连续重试还挂你拿到的 trace 就是它两次失败的完整现场。遇到疑难问题时跑命令前加DEBUG_TRACE1环境变量临时切到全量录制排查完再切回来不用反复改配置文件。screenshot: only-on-failure保留一张失败截图主要用于快速扫一眼以及和非技术同事沟通时有个图可以贴。video: off是我有意关掉的Trace 的信息量已经足够视频不仅占存储看的时候还浪费时间。如果你需要给别人看表象再单独开视频不迟。6.2 这套方案实际用起来的效果这套方案在跑了上千条用例的项目里稳定用了一整年偶发问题的定位时间从小时级降到了分钟级。Trace 录制本身不是魔法它只是把什么时间、在什么状态下、发生了什么请求和日志如实记录下来但就凭这一点足以让那些曾经的玄学问题变成有据可查的工程问题。最后再分享一个小技巧如果你负责的团队里有好几条流水线建议在流水线任务里把test-results/trace/**单独设成一个 artifact保留最近 30 天。这样即使当时没来得及看过了几天再回查历史失败用例依然能找回当时的完整现场。我吃过不少亏之后才意识到排障工具的可用性不仅取决于它能记录什么还取决于你能否在需要的时候快速找到那份记录。
返回列表