Playwright Test 中的 TestInfo 类详解:在测试运行时掌握状态、附件、快照与超时 Playwright Test 中的 TestInfo 类详解在测试运行时掌握状态、附件、快照与超时【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright在 Playwright Test 中每个测试函数、钩子beforeEach/afterEach/beforeAll/afterAll以及测试级 fixture 都会收到一个testInfo参数它是当前正在运行的测试的完整运行时上下文。本文基于 Playwright 仓库中的官方 API 文档与 TestInfoImpl 源码实现系统讲解TestInfo的每个属性与方法如何识别当前测试、对比实际/预期状态、条件性跳过或标记失败、管理重试与并行索引、动态调整超时以及正确落盘附件、临时文件与快照帮助你在编写测试与自定义 Reporter 时充分利用测试执行期的全部信息。TestInfo 概述它是从哪里来的TestInfo包含当前运行中测试的信息可用于测试函数、四个钩子以及 test-scoped fixture并提供控制测试执行的工具附加文件、更新测试超时、判断当前运行的是哪个测试、是否处于重试中等。import { test, expect } from playwright/test; test(basic test, async ({ page }, testInfo) { expect(testInfo.title).toBe(basic test); await page.screenshot(testInfo.outputPath(screenshot.png)); });从源码结构看testInfo的实际类型是 TestInfoImpl它在每个 worker 进程执行测试时由 workerMain.ts 的_runTest创建并绑定四个回调onStepBegin/onStepEnd/onAttach/onTestPaused用于把步骤事件和附件事件上报给测试框架Reporter 与 Trace Viewer 的数据来源之一。构造函数把test用例的元数据一次性写入title、titlePath、file、line、column、tags、fn、expectedStatus等见 testInfo.ts 构造函数。除第二个参数注入外测试体内还可以随时通过test.info()获取同一个实例由 testType.ts 提供非测试运行期调用会直接抛错快照断言相关的示例中即使用了test.info().snapshotPath(...)的写法。识别当前测试title、位置与归属属性类型含义testInfo.titlestring传给test(title, testFunction)的标题testInfo.titlePathArraystring从测试文件名开始的完整标题路径testInfo.filestring当前测试声明所在文件的绝对路径testInfo.lineint当前测试声明所在行号testInfo.columnint当前测试声明所在列号testInfo.testIdstring与 Reporter API 中 test case id 匹配的测试 idv1.32 起testInfo.fnfunction传给test(title, testFunction)的测试函数本身testInfo.projectFullProject来自配置文件的处理后 project 配置testInfo.configFullConfig来自配置文件的处理后全局配置在实现中file/line/column直接取自测试用例的locationtestInfo.ts因此即使测试被describe多层嵌套位置信息也始终指向test(...)声明处。titlePath则包含文件相对路径与各级describe标题这在自定义 Reporter 中做去重与归档时非常有用titlePath[0]是测试文件相对路径其余是各级标题。testInfo.fn暴露的是测试函数本体适合在 fixture 中判断是否为参数化测试或做函数级分析例如根据函数名做分支处理。实际状态与预期状态status 与 expectedStatus属性类型含义testInfo.statuspassed \| failed \| timedOut \| skipped \| interrupted实际状态。测试运行期间为undefined在afterEach钩子与 fixture 中才有值testInfo.expectedStatus同上不含timedOut/interrupted预期状态通常为passedtestInfo.errorTestInfoError?执行中抛出的第一个错误等于errors[0]testInfo.errorsArrayTestInfoError执行中抛出的全部错误testInfo.durationint测试耗时毫秒未结束前恒为 0可在afterEach中使用expectedStatus通常为passed但有两种例外被跳过的测试如通过test.skip为skipped被标记为预期失败test.fail的测试为failed。最典型的用法是在afterEach中比较实际状态与预期状态import { test, expect } from playwright/test; test.afterEach(async ({}, testInfo) { if (testInfo.status ! testInfo.expectedStatus) console.log(${testInfo.title} did not run as expected!); });从源码看expectedStatus的调整集中在两处一是 worker 处理文件级/用例级 annotation 时workerMain.ts 的processAnnotationskip/fixme置为skippedfail置为failed二是测试体内调用testInfo.skip()等运行时标注方法时见下文_modifier。失败时status由_failWithError写入超时会得到timedOut其余为failed同时把序列化后的错误压入errors数组——error属性只是errors[0]的快捷访问器testInfo.ts。运行时控制测试skip、fixme、fail、slowTestInfo提供与静态 APItest.skip等语义一致的运行时方法都支持两种调用形式无条件调用或method(condition, description)条件调用。方法行为testInfo.skip()/testInfo.skip(condition, description?)无条件/条件跳过当前测试测试立即中止testInfo.fixme()/testInfo.fixme(condition, description?)无条件/条件标记待修复测试立即中止testInfo.fail()/testInfo.fail(condition, description?)无条件/条件标记应当失败测试照常运行Playwright Test 断言它确实失败testInfo.slow()/testInfo.slow(condition, description?)无条件/条件标记慢速超时时间变为默认值的三倍这四个方法在实现上统一走_modifier四个方法都被transform.wrapFunctionWithLocation包裹因此description会自动带上调用位置的location信息写入annotationsReporter 能显示在哪里被跳过skip/fixme会先把expectedStatus置为skipped再抛出TestSkipError使测试立即中止fail把expectedStatus改为failed若已是 skipped 则不覆盖slow调用timeoutManager.slow()将超时乘以 3 倍。_modifier还会显式拦截一个常见误用在测试内调用test.skip(callback)传回调用形式会抛出带有正确示例的错误提示testInfo.ts。典型场景是运行时根据环境特征跳过test(only on linux, async ({ page }, testInfo) { testInfo.skip(process.platform ! linux, Linux-only feature); // ... });重试、并行与重复执行retry、parallelIndex、workerIndex、repeatEachIndex属性含义testInfo.retry重试编号。首次运行恒为 0第一次重试为 1依此类推testInfo.parallelIndex当前 worker 在0..workers-1之间的索引同时运行的 worker 保证互不相同。worker 重启后如失败后新进程沿用同一parallelIndextestInfo.workerIndex运行该测试的 worker 进程的唯一索引。worker 重启后新进程会获得新的唯一workerIndextestInfo.repeatEachIndex以--repeat-each命令行参数运行时的唯一重复索引见 test-cliretry在排查 flaky 测试时特别有用——重试前清理服务端状态import { test, expect } from playwright/test; test.beforeEach(async ({}, testInfo) { // You can access testInfo.retry in any hook or fixture. if (testInfo.retry 0) console.log(Retrying!); }); test(my test, async ({ page }, testInfo) { // Here we clear some server-side state when retrying. if (testInfo.retry) await cleanSomeCachesOnTheServer(); // ... });关于并行索引parallelIndex与workerIndex的区别在于前者标识并行槽位0 到workers - 1后者标识进程实例。worker 崩溃重启后新进程继承parallelIndex但获得新的workerIndex。两者也分别以环境变量process.env.TEST_PARALLEL_INDEX与process.env.TEST_WORKER_INDEX暴露在 workerMain.ts 中于 worker 启动时写入。详见 并行与分片 以及 重试。retry还会直接影响输出目录outputDir在重试/重复执行时会追加-retryN/-repeatN后缀testInfo.ts保证多次运行的产物互不覆盖。超时管理timeout 与 setTimeouttestInfo.timeout当前测试的超时毫秒0 表示不超时testInfo.setTimeout(timeout)修改当前正在运行的测试的超时0 表示不超时。超时通常配置在配置文件中但有时需要在运行时动态调整。典型例子是在beforeEach里为所有走到该钩子的测试统一延长 30 秒import { test, expect } from playwright/test; test.beforeEach(async ({ page }, testInfo) { // Extend timeout for all tests running this hook by 30 seconds. testInfo.setTimeout(testInfo.timeout 30000); });从源码看timeout是 TimeoutManager 的默认槽读取值testInfo.ts而setTimeout直接调用_timeoutManager.setTimeout(timeout)。另外testInfo.slow()调用的timeoutManager.slow()会将默认槽超时乘以 3 倍。更多超时机制参见 各种超时。标注与标签annotations 与 tagstestInfo.annotations是一个数组类型为type: Array{ type: string; // 标注类型例如 skip 或 fail description?: string; // 可选描述 location?: Location; // 可选标注在源码中的位置 }它汇总了三层来源的标注测试自身的标注、测试所属各级describe组的标注、以及测试文件级的标注。这与 workerMain 中的 annotation 处理逻辑 对应先处理测试自身annotations再叠加父级 suite 在运行中动态追加的标注。更多标注用法见 test annotations。testInfo.tagsv1.43 起返回作用于当前测试的标签数组例如[smoke]可用于在 fixture 中按标签启用不同行为。注意测试运行期间对该列表的修改对 Reporter 不可见因为事件在上报时已序列化。标签用法详见 tags 章节。附件attach 方法与 attachments 属性testInfo.attachments记录附加到当前测试的全部文件或 Buffer结构为type: Array{ name: string; // 附件名 contentType: string; // 供报告正确展示的内容类型如 application/json、image/png path?: string; // 可选附件在文件系统中的路径 body?: Buffer; // 可选替代文件使用的附件本体 }一些 Reporter 会展示测试附件。添加附件应使用testInfo.attach()而不是直接push到attachments数组。attach 的两种数据源body 与 pathimport { test, expect } from playwright/test; test(basic test, async ({ page }, testInfo) { await page.goto(https://playwright.dev); const screenshot await page.screenshot(); await testInfo.attach(screenshot, { body: screenshot, contentType: image/png }); });也可以附加由 API 返回的文件import { test, expect } from playwright/test; import { download } from ./my-custom-helpers; test(basic test, async ({}, testInfo) { const tmpPath await download(a); await testInfo.attach(downloaded, { path: tmpPath }); });参数说明参数说明name附件名会经过 sanitize 并作为落盘文件名前缀body附件本体string或Buffer与path互斥contentType报告展示用的内容类型省略时根据path推断字符串附件默认text/plainBuffer 附件默认application/octet-streampath文件系统上的附件文件路径与body互斥path与body必须二选一。重要提示attach会自动把附件文件复制到 Reporter 可访问的位置因此await完成后原文件可以安全删除。从源码看TestInfoImpl.attach做了三件事创建一条 category 为test.attach的步骤会出现在 Trace/报告中、通过normalizeAndSaveAttachment把附件归一化并拷贝到outputPath()下、再通过_attach将附件事件含 base64 化的 body经onAttach回调上报给框架。这也解释了为什么文档说可以安全删除原文件——框架内部已经复制了一份。每个测试独占的目录outputDir 与 outputPathtestInfo.outputDir本次测试运行独占的输出目录绝对路径各次运行互不冲突testInfo.snapshotDir本测试的快照输出目录绝对路径每个测试套件独占一个目录互不冲突。注意该属性不考虑snapshotPathTemplate配置模板会在此之上进一步插值。testInfo.outputPath(...pathSegments)返回outputDir内部的一个路径测试可安全地在其中写临时文件并保证并行测试之间互不干扰import { test, expect } from playwright/test; import fs from fs; test(example test, async ({}, testInfo) { const file testInfo.outputPath(dir, temporary-file.txt); await fs.promises.writeFile(file, Put some data to the dir/temporary-file.txt, utf8); });outputPath支持多段路径如testInfo.outputPath(relative, path, to, output)但结果路径必须仍位于该测试的outputDir即test-results/test-title之内否则会抛错。这一点在源码中由_getOutputPath中的getContainedPath校验实现越界时抛出The outputPath is not allowed outside of the parent directory。outputDir的构造规则testInfo.ts值得了解它由测试文件相对路径把/换成- 清洗后的完整测试标题拼成并依次追加 project id多 project 时、-retryN、-repeatN后缀最终挂在project.outputDir默认test-results之下。outputPath()首次调用时还会自动mkdirSync创建目录。快照路径snapshotPath 方法与 snapshotSuffixtestInfo.snapshotPath(...name)返回给定名称的快照文件路径。v1.53 起可以传入kind指定快照种类从而匹配对应断言使用的路径模板kind: screenshot对应expect(page).toHaveScreenshot(name)kind: aria对应expect(locator).toMatchAriaSnapshot(...)kind: snapshot对应expect(value).toMatchSnapshot(name)也是默认值。await expect(page).toHaveScreenshot(header.png); // Screenshot assertion above expects screenshot at this path: const screenshotPath test.info().snapshotPath(header.png, { kind: screenshot }); await expect(page.getByRole(main)).toMatchAriaSnapshot({ name: main.aria.yml }); // Aria snapshot assertion above expects snapshot at this path: const ariaSnapshotPath test.info().snapshotPath(main.aria.yml, { kind: aria }); expect(some text).toMatchSnapshot(snapshot.txt); // Snapshot assertion above expects snapshot at this path: const snapshotPath test.info().snapshotPath(snapshot.txt); expect(some text).toMatchSnapshot([dir, subdir, snapshot.txt]); // Snapshot assertion above expects snapshot at this path: const nestedPath test.info().snapshotPath(dir, subdir, snapshot.txt);参数细节...name快照名或定义快照文件路径的路径段。同一测试文件中同名的快照预期相同传入kind时不支持多个 name 段kind决定使用哪个快照路径模板详见TestConfig.snapshotPathTemplate配置默认snapshot。实现上snapshotPath会校验kind取值非法值抛unknown kind错误再交给_resolveSnapshotPaths完成解析对screenshot/aria/snapshot分别选择expect.toHaveScreenshot.pathTemplate、expect.toMatchAriaSnapshot.pathTemplate或全局snapshotPathTemplatearia 快照在未配置时回退到不含 projectName/snapshotSuffix 插值段的默认模板。随后_applyPathTemplate把{testDir}、{snapshotDir}、{testFileName}、{arg}、{projectName}、{snapshotSuffix}等占位符替换为实际值——这正是kind能精确复现断言落盘位置的原因。testInfo.snapshotSuffix用于在多种测试配置之间区分快照例如让snapshotSuffix process.platform按平台使用不同快照。官方已不建议继续依赖该属性推荐改用TestConfig.snapshotPathTemplate配置快照路径。快照机制详见 test snapshots。小结TestInfo 能力速查能力成员典型场景识别测试title、titlePath、file、line、column、testId、fn自定义 Reporter、日志定位结果判定status、expectedStatus、error、errors、durationafterEach中告警、统计耗时运行时控制skip、fixme、fail、slow均可带 condition/description按环境/数据条件动态调整执行重试与并行retry、parallelIndex、workerIndex、repeatEachIndex另有TEST_RETRY、TEST_PARALLEL_INDEX、TEST_WORKER_INDEX环境变量重试清理、按 worker 分片资源超时timeout、setTimeout钩子内统一延长超时标注annotations、tags汇总文件级/组级/测试级标注产物落盘attach、attachments、outputDir、outputPath截图/日志附件、并行安全的临时文件快照snapshotDir、snapshotPath含kind、snapshotSuffix复现断言的快照路径、多配置区分快照以上所有 API 自 v1.10 起可用tags自 v1.43、snapshotPath的kind选项自 v1.53、testId自 v1.32。完整的类型声明见 packages/playwright/types/test.d.ts运行实现见 packages/playwright/src/worker/testInfo.ts。【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

最新新闻

FurMark 1.6.5烤机全解读:从显卡压力测试原理到稳定性判断

FurMark 1.6.5烤机全解读:从显卡压力测试原理到稳定性判断

简介:FurMark 1.6.5 是一款基于 OpenGL 的显卡压力测试与稳定性检测工具,面向硬件评测用户、游戏玩家及超频爱好者,用于在高负载渲染场景下检验显卡性能极限与稳定性。软件支持分辨率、反锯齿、窗口/全屏等参数自定义,可通过批处理…

2026/9/7 11:18:22
嵌入式学习2个月就放弃?不是难,而是你的路线错了

嵌入式学习2个月就放弃?不是难,而是你的路线错了

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 11:18:22
触摸屏报警“急停开关被按下”:PLC与HMI故障排查指南

触摸屏报警“急停开关被按下”:PLC与HMI故障排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 11:18:22
AI Agent实战:用Grok 4.6搭建自动视频制作全流程

AI Agent实战:用Grok 4.6搭建自动视频制作全流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 11:18:22
AI写PLC只能算L1?PLC Coding五级能力模型解读与工程师实战指南

AI写PLC只能算L1?PLC Coding五级能力模型解读与工程师实战指南

最近工业自动化圈子里聊得最热闹的话题,不是哪家新出了旗舰PLC,也不是谁的伺服又把响应带宽拉高了几毫秒,而是AI到底能不能进车间、能不能写PLC程序。我自己也拿市面上的几款AI工具试过,让它写个电机正反转、写个星三角启动&#…

2026/9/7 11:18:22
CMSIS-DSP源码审计与工业固件性能优化实战指南

CMSIS-DSP源码审计与工业固件性能优化实战指南

两年前我在调试一台变频器样机时,遇到过一个让我失眠一周的问题:整机在EMC预测试阶段,传导发射在2MHz附近反复超限。硬件工程师怀疑是开关电源布线,重新布局了三次仍无改善。后来我无意中看了一眼电流环的代码,发现负责…

2026/9/7 11:13:22