news 2026/9/7 2:56:29

Playwright Test TestProject 配置详解:多项目编排、依赖链与并行控制的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Playwright Test TestProject 配置详解:多项目编排、依赖链与并行控制的完整指南

Playwright Test TestProject 配置详解:多项目编排、依赖链与并行控制的完整指南

【免费下载链接】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 仓库的 API 文档docs/src/test-api/class-testproject.md展开,系统讲解TestProject的全部配置项及其解析优先级,并结合packages/playwright/src/common/config.ts等源码实现,说明多浏览器/多设备项目如何声明、dependencies/teardown如何构成执行链、以及workersfullyParallel等并行选项在底层是如何生效的。读完后你应能独立编写可复制运行的playwright.config.ts,并准确解释每个项目级参数的默认值与覆盖规则。

核心概念:TestProject 与 FullProject 的区别

Playwright Test 支持在一次运行中同时跑多个测试项目(project),典型场景是同一套测试在多个浏览器、桌面/移动配置下分别执行。文档 class-testproject.md 定义了配置文件里项目的写法:

  • TestProject描述的是配置文件中的项目格式,即你在playwright.config.tsprojects数组中写的每一项;
  • 运行时想访问解析后的完整配置,应使用FullProject(见 class-fullproject.md)。

项目通过 [property: TestConfig.projects] 声明,位置是配置文件。所有TestProject的属性同样可以写在顶层TestConfig中,此时被所有项目共享。从源码看,这一"项目级覆盖顶层"的解析逻辑集中在 FullProjectInternal 构造函数,每个属性都通过takeFirst链取值,例如:

// packages/playwright/src/common/config.ts testMatch: takeFirst(projectConfig.testMatch, config.testMatch, '**/*.@(spec|test).?(c|m)[jt]s?(x)'), timeout: takeFirst(configCLIOverrides.debug === 'inspector' ? 0 : undefined, configCLIOverrides.timeout, projectConfig.timeout, config.timeout, defaultTimeout), use: mergeObjects(config.use, projectConfig.use, configCLIOverrides.use),

可以由此确认优先级顺序为:命令行参数 > 项目级配置 > 顶层配置 > 内置默认值。另外 config.ts#L132-L134 还揭示了兜底规则:如果projects完全没写,Playwright 会把整个顶层配置当作唯一的项目([{ ...userConfig, workers: undefined }]),即顶层写的usetimeout等直接生效。

多浏览器 + 多设备项目的完整示例

下面这份配置让全部测试在 Chromium、Firefox、WebKit 的桌面版和移动版上各跑一遍(继承自 class-testproject.md 的示例):

import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ // Options shared for all projects. timeout: 30000, use: { ignoreHTTPSErrors: true, }, // Options specific to each project. projects: [ { name: 'chromium', use: devices['Desktop Chrome'], }, { name: 'firefox', use: devices['Desktop Firefox'], }, { name: 'webkit', use: devices['Desktop Safari'], }, { name: 'Mobile Chrome', use: devices['Pixel 5'], }, { name: 'Mobile Safari', use: devices['iPhone 12'], }, ], });

顶层的timeout: 30000use.ignoreHTTPSErrors被所有项目共享;每个项目仅声明自己的nameusedevices预设来自 TestOptions 的设备描述符)。注意 FullProjectInternal 中对use的处理是mergeObjects(config.use, projectConfig.use, configCLIOverrides.use),即项目use按字段合并在顶层use之上,而不是整体替换——这正是多项目配置能"只写差异"的原因。

项目执行顺序:dependencies 与 teardown

property: dependencies(since v1.31,type: ?Array<string>)

列出必须在本项目任何测试运行之前先跑完的项目。它最常用于把全局 setup 组织成"以测试形式存在的动作"——这样 setup 步骤能在测试报告中展示、并能产生 trace 等工件。传入--no-deps命令行参数可忽略依赖,行为等同于未声明(该参数在 program.ts#L225 中注册为--no-deps)。

import { defineConfig } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'setup', testMatch: /global.setup\.ts/, }, { name: 'chromium', use: devices['Desktop Chrome'], dependencies: ['setup'], }, { name: 'firefox', use: devices['Desktop Firefox'], dependencies: ['setup'], }, { name: 'webkit', use: devices['Desktop Safari'], dependencies: ['setup'], }, ], });

从源码结构看,依赖在配置加载阶段由 resolveProjectDependencies 解析:依赖名必须能唯一匹配某个项目,否则直接抛出Project 'xxx' depends on unknown project 'yyy'或"依赖名不唯一"的错误,属于配置期校验而非运行期失败。

property: teardown(since v1.34,type: ?string)

指向一个在本项目及其所有依赖项目都结束之后才运行的项目名,适合做资源清理。--no-deps同样会忽略teardown。常见模式是 "setup + 对应 teardown":

import { defineConfig } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'setup', testMatch: /global.setup\.ts/, teardown: 'teardown', }, { name: 'teardown', testMatch: /global.teardown\.ts/, }, { name: 'chromium', use: devices['Desktop Chrome'], dependencies: ['setup'], }, { name: 'firefox', use: devices['Desktop Firefox'], dependencies: ['setup'], }, { name: 'webkit', use: devices['Desktop Safari'], dependencies: ['setup'], }, ], });

teardown挂在setup项目上,意味着"当依赖 setup 的所有项目都跑完后"才执行 teardown,这与 全局 setup/teardown 文档 描述的机制相衔接,但粒度精确到项目依赖链。

测试文件选择:testDir、testMatch、testIgnore、respectGitIgnore

property: testDir(since v1.10,type: ?string)

递归扫描测试文件的目录,默认为配置文件所在目录。每个项目可以指向不同目录。示例:smoke 测试在三种浏览器上跑,其余测试只在稳定版 Chrome 上跑:

import { defineConfig } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'Smoke Chromium', testDir: './smoke-tests', use: { browserName: 'chromium' }, }, { name: 'Smoke WebKit', testDir: './smoke-tests', use: { browserName: 'webkit' }, }, { name: 'Smoke Firefox', testDir: './smoke-tests', use: { browserName: 'firefox' }, }, { name: 'Chrome Stable', testDir: './', use: { browserName: 'chromium', channel: 'chrome', }, }, ], });

源码中testDir的解析为 takeFirst(项目级 → 顶层 → configDir),testDir同时是snapshotDir的默认值(见下文)。

property: testMatch(since v1.10,type: ?string | RegExp | Array)

只有匹配其中任一模式的文件才会被当作测试文件执行,匹配针对绝对文件路径进行,字符串按 glob 模式处理。默认 glob 为**/*.@(spec|test).?(c|m)[jt]s?(x)——即带.test.spec后缀的 JS/TS 文件,如login-screen.wrong-credentials.spec.ts。这一默认值在 config.ts#L193 中可直接得到印证。

property: testIgnore(since v1.10,type: ?string | RegExp | Array)

testMatch相反:匹配任一模式的文件不会作为测试文件执行。例如'**/test-assets/**'会忽略test-assets目录下所有文件。

property: respectGitIgnore(since v1.45,type: ?boolean)

是否在搜索测试文件时跳过.gitignore中的条目。默认行为(源码 config.ts#L206):当既没有显式指定顶层testDir也没有指定项目级testDir时,Playwright 会忽略匹配.gitignore的测试文件;该选项用于覆盖此默认行为。

并行与重复执行:fullyParallel、workers、repeatEach

property: fullyParallel(since v1.10,type: ?boolean)

Playwright Test 通过同时运行多个 worker 进程实现并行;默认并行粒度是测试文件——同一文件内的测试按顺序在同一个 worker 里执行。将本项目设为fullyParallel: true后,所有文件中的所有测试都会并发调度。源码中其解析优先级为命令行 → 项目 → 顶层(config.ts#L200),且 debug 模式下会被统一改写为串行(见下)。

property: workers(since v1.52,type: ?int | string)

限制本项目可用的最大并发 worker 数,也支持写成逻辑 CPU 核数的百分比,例如'50%'。典型场景:某项目的所有测试共享一个测试账号,无法并行,把它的workers设为 1 即可防止并发使用共享资源。

注意:全局 [property: TestConfig.workers] 限制的是worker 数,而本项在总限额内进一步限制单项目占用;不设置时单项目无额外上限。

import { defineConfig } from '@playwright/test'; export default defineConfig({ workers: 10, // total workers limit projects: [ { name: 'runs in parallel' }, { name: 'one at a time', workers: 1, // workers limit for this project }, ], });

底层解析实现在 resolveWorkers:百分比按os.cpus().length计算并向下取整(Math.max(1, Math.floor(cpus * percent/100))),非正数或非法值会抛出Workers ... must be a number or percentage错误。全局 workers 的默认值是'50%'(config.ts#L110),另外在 debug(--debug/--pause)模式下项目级 workers 会被强制为 1(config.ts#L208-L209)。

property: repeatEach(since v1.10,type: ?int)

每个测试重复执行的次数,默认 1(config.ts#L186),用于调试不稳定(flaky)测试。可用 [property: TestConfig.repeatEach] 统一设置。

测试过滤:grep 与 grepInvert

property: grep(since v1.10,type: ?RegExp | Array<RegExp>)

只运行标题匹配任一模式的测试。正则匹配的目标字符串由以下部分按空格拼接而成:项目名、测试文件名、test.describe名(如有)、测试名、测试 tags,例如chromium my-test.spec.ts my-suite my-test。因此可以精确地"只在某个项目上跑某类测试"。同样可以全局设置,或通过命令行的-g选项传入。该选项也是测试打 tag的主要手段。

property: grepInvert(since v1.10,type: ?RegExp | Array<RegExp>)

grep相反:只运行标题不匹配任一模式的测试。对应命令行选项为--grep-invert,同样适合配合 tag 机制 排除某些测试。

断言与快照:expect、ignoreSnapshots、snapshotDir、snapshotPathTemplate、outputDir

property: expect(since v1.10,type: ?Object)

expect断言库的项目级配置,可用 [property: TestConfig.expect] 全局设置。各字段及默认值:

字段类型 / 默认值说明
timeoutint,默认 5000ms异步 expect 匹配器的默认超时(毫秒)
toHaveScreenshotObject[method: PageAssertions.toHaveScreenshot#1] 的配置
toMatchAriaSnapshotObject[method: LocatorAssertions.toMatchAriaSnapshot#2] 的配置
toMatchSnapshotObject[method: SnapshotAssertions.toMatchSnapshot#1] 的配置
toPassObjectexpect(value).toPass() 的配置

expect.toHaveScreenshot子项:

字段类型 / 默认值说明
thresholdfloat同一像素可接受的感知色差,0(严格)到1(宽松);"pixelmatch"比较器在 YIQ 色彩空间计算色差,默认0.2
maxDiffPixelsint允许的最大差异像素数,默认未设置
maxDiffPixelRatiofloat允许的差异像素占比(01),默认未设置
animations"allow"|"disabled"见 [method: Page.screenshot] 的animations,默认"disabled"
caret"hide"|"initial"caret,默认"hide"
scale"css"|"device"scale,默认"css"
stylePathstring | Array<string>额外注入的样式表,见Page.screenshot.style
pathTemplatestring控制截图存放位置的模板,语义同 [property: TestProject.snapshotPathTemplate]
timeoutint该断言的超时,默认取全局 expect timeout;设为0表示禁用超时

expect.toMatchAriaSnapshot子项:pathTemplate(aria 快照位置模板)、children"contain"|"equal"|"deep-equal",控制快照根的子节点如何与真实可访问性树匹配,等价于在每份 aria 快照模板顶部加一个/children属性,单份快照可用显式/children覆盖)。

expect.toMatchSnapshot子项:thresholdmaxDiffPixelsmaxDiffPixelRatio,语义同上。

expect.toPass子项:timeout(毫秒)、intervals(探测间隔数组,毫秒)。

源码层面,FullProjectInternal 中expecttakeFirst(projectConfig.expect, config.expect, {})取整个对象;若配置了expect.toHaveScreenshot.stylePath,会被解析为相对 configDir 的绝对路径。

property: ignoreSnapshots(since v1.44,type: ?boolean)

跳过快照类断言(toMatchSnapshot()toHaveScreenshot())。示例:只让 Chromium 项目做截图断言:

import { defineConfig } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'chromium', use: devices['Desktop Chrome'] }, { name: 'firefox', use: devices['Desktop Firefox'], ignoreSnapshots: true }, { name: 'webkit', use: devices['Desktop Safari'], ignoreSnapshots: true }, ], });

解析链为 CLI → 项目 → 顶层 →false(config.ts#L198),因此也可以传--ignore-snapshots全局生效。

property: snapshotDir(since v1.10,type: ?string)

toMatchSnapshot创建快照文件的基目录(相对配置文件),默认是 [property: TestProject.testDir](config.ts#L191)。每个测试文件有独立快照目录,可通过 [property: TestInfo.snapshotDir]、[method: TestInfo.snapshotPath] 访问。例如snapshotDir: 'snapshots'时,测试文件a.spec.js的快照目录解析为snapshots/a.spec.js-snapshots

property: snapshotPathTemplate(since v1.28)

该属性与顶层TestConfig.snapshotPathTemplate共用同一说明(文档通过 include 引入,见 class-testconfig.md),用于模板化地控制快照/截图/aria 快照文件的位置,支持{arg}占位。项目级值优先于顶层(config.ts#L172)。快照路径的最终解析顺序可以在 testInfo.ts#L611-L616 中确认:expect.toHaveScreenshot.pathTemplate→ 项目/顶层snapshotPathTemplate→ 内置旧版模板,aria 快照同理。

property: outputDir(since v1.10,type: ?string)

测试执行期间产生的文件(截图、视频、trace 等)的输出目录,默认为<package.json 目录>/test-results(config.ts#L183)。该目录在运行开始时被清理;每次运行测试会在其中创建唯一子目录,保证并行测试互不冲突,可通过 [property: TestInfo.outputDir]、[method: TestInfo.outputPath] 访问:

import { test, expect } from '@playwright/test'; import fs from 'fs'; test('example test', async ({}, testInfo) => { const file = testInfo.outputPath('temporary-file.txt'); await fs.promises.writeFile(file, 'Put some data to the file', 'utf8'); });

超时、重试、命名与元数据

property: timeout(since v1.10,type: ?int)

每个测试的超时,默认 30 秒。它是所有测试的基础超时:单个测试可用 [method: Test.setTimeout] 覆盖,文件/组级可用 [method: Test.describe.configure] 覆盖;顶层用 [property: TestConfig.timeout] 统一设置。

property: retries(since v1.10,type: ?int)

失败测试的最大重试次数,默认 0(config.ts#L187),更多机制见测试重试。可用 [method: Test.describe.configure] 对特定文件/组调整,或 [property: TestConfig.retries] 全局设置。

property: name(since v1.10,type: ?string)

项目名会显示在报告和运行过程中。文档特别警告:Playwright 会多次执行配置文件,不要在配置中动态生成不稳定值(比如每次运行都不同的随机 ID)。(从源码看 config.ts#L140-L154 还会在重名时给项目 id 追加数字后缀以保证唯一性。)

property: metadata(since v1.10,type: ?Metadata)

以 JSON 序列化形式直接写入测试报告的元数据,便于在 HTML 报告等展示环境信息。

项目选项汇总与解析优先级速查

属性类型默认值引入版本
dependencies?Array<string>无依赖v1.31
expect?Object{}v1.10
fullyParallel?booleanfalsev1.10
grep/grepInvert?RegExp | Array<RegExp>全部通过 / 不取反v1.10
ignoreSnapshots?booleanfalsev1.44
metadata?Metadata{}v1.10
name?stringv1.10
outputDir?string<package.json 目录>/test-resultsv1.10
repeatEach?int1v1.10
respectGitIgnore?boolean未显式指定 testDir 时为 truev1.45
retries?int0v1.10
teardown?stringv1.34
testDir?string配置文件目录v1.10
testIgnore?string | RegExp | Array[]v1.10
testMatch?string | RegExp | Array**/*.@(spec\|test).?(c\|m)[jt]s?(x)v1.10
timeout?int30000 msv1.10
use?TestOptions合并自顶层usev1.10
workers?int | string无项目级上限(全局默认'50%'v1.52

结合 config.ts 的takeFirst链可以确认统一规则:命令行覆盖 > 项目配置 > 顶层 TestConfig > 内置默认,其中use是唯一按字段深度合并的选项。掌握这张表和优先级,就覆盖了TestProject在配置、过滤、快照、并行四个维度的全部控制点;项目间如何共享 fixture 与选项继承,可继续阅读 test-configuration.md 与 test-use-options-js.md。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 2:56:17

蓝牙音箱PCBA开发周期真相:从7天出样到量产还差多远?

/* 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 2:49:03

DeepSeek Harness 接入 Codex 实战:读图链路、配置与报错排查

有人在技术群里问&#xff1a;DeepSeek Harness 能读图了&#xff1f;装完之后&#xff0c;是不是可以直接在 Codex 里丢一张报错截图、贴一份设计稿&#xff0c;让 DeepSeek 看图改代码&#xff1f;我正好在做本地模型链路实验&#xff0c;就顺手把 Harness 装起来&#xff0c…

作者头像 李华
网站建设 2026/9/7 2:48:56

腾讯云 AI Skills 实战:从零构建可编排的 Agent 技能体系

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

作者头像 李华