Playwright 全局 Setup 与 Teardown 实战:项目依赖(dependencies)与 globalSetup 两种方案的完整解析
Playwright 全局 Setup 与 Teardown 实战项目依赖dependencies与 globalSetup 两种方案的完整解析【免费下载链接】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 官方文档 Global setup and teardown 展开系统讲解在整套测试运行之前执行一次性初始化如创建数据库、登录获取认证状态、运行结束后执行清理如删除测试数据的两种官方方案基于项目依赖Project Dependencies推荐与基于globalSetup/globalTeardown配置项。读完本文后你将掌握两种方案的完整配置写法、运行时的调度顺序与失败语义并能在自己的 Playwright 项目中落地可追溯、可重用的全局初始化逻辑。一、两种方案总览与功能对比Playwright 测试运行器提供两条全局 setup/teardown 路径项目依赖Project Dependencies推荐定义一个在前置运行完成之前不会被执行的项目把全局初始化动作写成普通测试。该方案与 Playwright 测试运行器集成度更高——HTML 报告中会把全局 setup 展示为独立项目、可以录制 trace、可以使用 fixture。globalSetup/globalTeardown配置项在 配置中指定 setup 文件路径该文件导出一个接收FullConfig对象的函数在所有测试开始前执行一次对应地globalTeardown在所有测试结束后执行一次。两种方案的能力差异如下继承自原文档对比表功能项目依赖推荐globalSetup配置项在所有测试之前运行✅ 是✅ 是HTML 报告中可见✅ 展示为独立项目❌ 不展示Trace 录制✅ 完整 trace 可用❌ 不支持Playwright fixture✅ 完整支持❌ 不支持浏览器管理✅ 通过browserfixture❌ 需完全手动browserType.launch()并行与重试✅ 通过标准配置支持❌ 不适用headless、testIdAttribute等配置项✅ 自动生效❌ 被忽略二、方案一项目依赖Project DependenciesTestProject.dependencies是一个项目名字符串数组表示“哪些项目必须先于本项目运行”。官方类型定义中明确写道利用 dependencies 可以让全局 setup 产出 trace 等测试产物。2.1 配置 setup 项目在 playwright.config.ts 中新增一个名为setup db的项目用testMatch匹配global.setup.ts文件import { defineConfig } from playwright/test; export default defineConfig({ testDir: ./tests, // ... projects: [ { name: setup db, testMatch: /global\.setup\.ts/, }, // { // other project // } ] });然后给依赖它的项目加上dependencies属性传入上一步定义的项目名import { defineConfig, devices } from playwright/test; export default defineConfig({ testDir: ./tests, // ... projects: [ { name: setup db, testMatch: /global\.setup\.ts/, }, { name: chromium with db, use: { ...devices[Desktop Chrome] }, dependencies: [setup db], }, ] });本例中chromium with db项目依赖于setup db项目。接着创建 setup 测试文件放在测试目录下注意setup/teardown 代码必须通过test()函数定义为普通测试import { test as setup } from playwright/test; setup(create new database, async ({ }) { console.log(creating new database...); // Initialize the database });业务测试保持不变import { test, expect } from playwright/test; test(menu, async ({ page }) { // Your test that depends on the database });2.2 配置 teardownsetup 项目可以通过teardown属性指定一个清理项目它会在所有依赖该项目的项目运行完成之后执行。给 setup 项目加上teardown: cleanup db并新增cleanup db项目匹配global.teardown.tsimport { defineConfig } from playwright/test; export default defineConfig({ testDir: ./tests, // ... projects: [ { name: setup db, testMatch: /global\.setup\.ts/, teardown: cleanup db, }, { name: cleanup db, testMatch: /global\.teardown\.ts/, }, { name: chromium, use: { ...devices[Desktop Chrome] }, dependencies: [setup db], }, ] });创建tests/global.teardown.ts用于在所有测试结束后删除数据库中的数据import { test as teardown } from playwright/test; teardown(delete database, async ({ }) { console.log(deleting test database...); // Delete the database });2.3 测试过滤对依赖的影响所有测试过滤选项——--grep/--grep-invert、--shard、命令行按位置直接过滤、test.only()——直接选择的是要执行的“主测试”。如果被选中的测试属于某个带依赖的项目那么它所有依赖项目中的测试也都会执行。可以传入--no-deps命令行选项来忽略所有依赖和 teardown此时只运行被直接选中的项目。官方dependencies类型定义的文档注释中也印证了这一点传入--no-deps参数会“表现得像没有指定依赖一样”。2.4 源码级实现项目依赖是如何被调度的运行器对依赖关系的处理集中在 projectUtils.ts闭包构建buildProjectsClosure()从每个顶层项目出发递归展开deps与teardown得到“顶级项目/依赖项目”的映射递归深度超过 100 层时抛出Circular dependency detected between projects.即项目间循环依赖会被显式报错见 projectUtils.ts#L93-L116。teardown 到 setup 的反向映射buildTeardownToSetupsMap()把“哪个 teardown 服务于哪些 setup 项目”建为 Map见 projectUtils.ts#L81-L91teardown 项目因此也能被纳入执行计划。阶段Phase划分在 tasks.ts 的createPhasesTask()中运行器把“所有依赖均已在之前阶段处理完毕”的项目归入同一阶段按阶段串行推进。值得注意的一个细节当配置了maxFailures时teardown 项目被放入独立阶段且标记为ignoreMaxFailures——即使测试失败数达到上限清理项目依然会运行保证 setup 产生的副作用数据库、临时资源等不会被跳过清理见 tasks.ts#L374-L378。环境变量继承在 tasks.ts 的createRunTestsTask()中每个项目执行时会从它的deps与关联 setup 继承额外环境变量extraEnvByProjectId并跳过“依赖项目中有失败项”的项目——依赖项目失败时下游项目不会被调度但 teardown 仍会执行。这也解释了为什么推荐用项目依赖setup/teardown 是普通测试因此天然拥有 fixture、重试、trace 和报告可见性。三、方案二globalSetup与globalTeardown配置项globalSetup选项用于在所有测试运行前设置一次性的环境。setup 文件必须导出一个单一函数该函数接收一个 config 对象FullConfig类型只运行一次。类型定义见 test.d.ts#L1317-L1358两个选项都接受string | Arraystring即支持传入多个 setup/teardown 文件路径。对应地globalTeardown在所有测试结束后运行一次。另一种等价写法是让globalSetup返回一个函数该返回值会被当作全局 teardown 使用。global setup 产出的数据端口号、认证 token 等可以通过环境变量传递给测试。注意globalSetup/globalTeardown缺少一些能力——见上文对比表。建议优先使用项目依赖以获得完整功能支持。import { defineConfig } from playwright/test; export default defineConfig({ globalSetup: require.resolve(./global-setup), globalTeardown: require.resolve(./global-teardown), });3.1 示例登录一次并复用认证状态下面的示例使用baseURL与storageState配置项在 global setup 中完成一次登录把认证状态落盘供所有测试复用import { chromium, type FullConfig } from playwright/test; async function globalSetup(config: FullConfig) { const { baseURL, storageState } config.projects[0].use; const browser await chromium.launch(); const page await browser.newPage(); await page.goto(baseURL!); await page.getByLabel(User Name).fill(user); await page.getByLabel(Password).fill(password); await page.getByText(Sign in).click(); await page.context().storageState({ path: storageState as string }); await browser.close(); } export default globalSetup;在配置文件中指定globalSetup、baseURL与storageStateimport { defineConfig } from playwright/test; export default defineConfig({ globalSetup: require.resolve(./global-setup), use: { baseURL: http://localhost:3000/, storageState: state.json, }, });测试启动时即已处于登录态因为storageState已由 global setup 填充import { test } from playwright/test; test(test, async ({ page }) { await page.goto(/); // You are signed in! });仓库自带的 认证指南 以及测试套件中 globalSetup should work for auth 用例global setup 中写入localStorage测试中验证已存在印证了这一流程。3.2 通过环境变量向测试传递任意数据在 global setup 中通过process.env设置的属性可以在测试内读取import type { FullConfig } from playwright/test; async function globalSetup(config: FullConfig) { process.env.FOO some data; // Or a more complicated data structure as JSON: process.env.BAR JSON.stringify({ some: data }); } export default globalSetup;import { test } from playwright/test; test(test, async ({ page }) { // environment variables which are set in globalSetup are only available inside test(). const { FOO, BAR } process.env; // FOO and BAR properties are populated. expect(FOO).toEqual(some data); const complexData JSON.parse(BAR); expect(BAR).toEqual({ some: data }); });注意文档中的措辞global setup 设置的环境变量只在test()内部可用因为测试运行在独立 worker 进程中环境通过运行器注入传递。此外setup/teardown 文件本身也能访问到PLAYWRIGHT_TEST1环境变量这在 globalSetup and globalTeardown should have PLAYWRIGHT_TEST1 用例中被显式验证。3.3 捕获 global setup 失败时的 traceglobalSetup方案本身不在报告中展示见对比表但仍可手动录制 trace 以便排查 setup 失败在 setup 中调用 tracing.start 开启录制并在错误抛出前确保调用tracing.stop()——用try...catch包裹 setup 逻辑即可import { chromium, type FullConfig } from playwright/test; async function globalSetup(config: FullConfig) { const { baseURL, storageState } config.projects[0].use; const browser await chromium.launch(); const context await browser.newContext(); const page await context.newPage(); try { await context.tracing.start({ screenshots: true, snapshots: true }); await page.goto(baseURL!); await page.getByLabel(User Name).fill(user); await page.getByLabel(Password).fill(password); await page.getByText(Sign in).click(); await context.storageState({ path: storageState as string }); await context.tracing.stop({ path: ./test-results/setup-trace.zip, }); await browser.close(); } catch (error) { await context.tracing.stop({ path: ./test-results/failed-setup-trace.zip, }); await browser.close(); throw error; } } export default globalSetup;3.4 源码级实现执行顺序、返回函数 teardown 与容错globalSetup/globalTeardown的任务构建与执行逻辑在 tasks.ts任务装配顺序createGlobalSetupTasks()生成的任务依次为“清理输出目录 → 插件 setup → 反转排列的 globalTeardown 任务 → globalSetup 任务”。teardown 任务被反转放在最外层、setup 任务放在最内层从而保证 teardown 在全部 setup 之后、按声明顺序执行见 tasks.ts#L153-L160。返回函数即 teardowncreateGlobalSetupTask()中setup 钩子的返回值若为函数会在对应任务的teardown阶段执行——这正是“让globalSetup返回一个函数作为全局 teardown”的实现见 tasks.ts#L205-L222。同步函数也支持钩子不要求必须是 async 函数await setupHook(config.config)对同步返回值同样生效。globalSetup 必须导出单一函数导出非函数时会抛出file must export a single function.错误对应测试 globalSetup should throw when passed non-function。仓库测试套件 global-setup.spec.ts 对这套机制的行为边界有非常明确的验证值得作为使用参考行为测试依据基本顺序setup → 测试 → teardownglobalSetup and globalTeardown should work测试失败后globalTeardown依然执行globalTeardown runs after failuresglobal setup 超时时 teardown 仍会运行globalTeardown still runs when globalSetup times outsetup 出错会导致测试完全不执行globalSetup error should prevent tests from executing多文件时setup 按声明顺序执行setup 返回的清理回调按声明的反序执行teardown 文件按声明顺序执行globalSetup should support multiplesetup 回调失败不影响globalTeardown执行globalTeardown runs even if callback failed支持从node_modules引用如globalSetup: my-global-setupglobalSetup should allow requiring a package from node_modules这些用例共同确认了globalSetup方案的失败语义setup 失败会阻止测试执行而 teardown 在绝大多数路径测试失败、setup 超时、回调报错下都会被执行与“清理必须发生”的设计意图一致。四、方案选择建议综合原文档对比表与源码实现选型可以遵循以下原则默认优先项目依赖setup/teardown 是普通测试自动获得 fixture、trace、报告可见性与重试能力headless、testIdAttribute等项目配置项自动生效依赖失败会正确阻止下游项目运行且maxFailures不会跳过 teardown 清理。globalSetup适合轻量、无浏览器场景如设置环境变量、启动一次性服务、生成临时资源。若需要浏览器操作如登录注意所有配置项需手动从FullConfig读取且需自行处理 trace见 3.3 节的 try...catch 模式。数据传递globalSetup方案用process.env向测试传值JSON 字符串化复杂结构项目依赖方案则可用 fixture 与配置项如storageState获得更类型安全的传递方式。更完整的实战示例可参考仓库中的 认证指南其中演示了用项目依赖复用登录态的完整写法官方文档同时提及了“利用项目依赖复用登录”的官方博客文章与 v1.31 发布演示视频作为延伸阅读详见原文档 More examples 小节。五、小结Playwright 的全局 setup/teardown 提供“项目依赖”与“globalSetup/globalTeardown”两条路径。前者把初始化/清理写成普通测试项目借助 dependencies 与 teardown 机制 融入标准测试调度阶段划分、失败传递、--no-deps过滤功能完整且可追溯后者以 tasks.ts 中的全局钩子任务 实现语义简单、支持多文件与返回函数式 teardown但缺少 fixture、trace 与报告集成。理解两者的运行顺序与失败语义尤其是 teardown 的“总是执行”保证是构建稳定 CI 环境的关键。【免费下载链接】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),仅供参考
