Playwright 全局 Setup 与 Teardown 实战:项目依赖(dependencies)与 globalSetup 两种方案的完整解析
2026/9/7 18:09:19 网站建设 项目流程

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 路径:

  1. 项目依赖(Project Dependencies,推荐):定义一个在前置运行完成之前不会被执行的项目,把全局初始化动作写成普通测试。该方案与 Playwright 测试运行器集成度更高——HTML 报告中会把全局 setup 展示为独立项目、可以录制 trace、可以使用 fixture。
  2. globalSetup/globalTeardown配置项:在 配置中指定 setup 文件路径,该文件导出一个接收FullConfig对象的函数,在所有测试开始前执行一次;对应地,globalTeardown在所有测试结束后执行一次。

两种方案的能力差异如下(继承自原文档对比表):

功能项目依赖(推荐)globalSetup(配置项)
在所有测试之前运行✅ 是✅ 是
HTML 报告中可见✅ 展示为独立项目❌ 不展示
Trace 录制✅ 完整 trace 可用❌ 不支持
Playwright fixture✅ 完整支持❌ 不支持
浏览器管理✅ 通过browserfixture❌ 需完全手动browserType.launch()
并行与重试✅ 通过标准配置支持❌ 不适用
headlesstestIdAttribute等配置项✅ 自动生效❌ 被忽略

二、方案一:项目依赖(Project Dependencies)

TestProject.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 配置 teardown

setup 项目可以通过teardown属性指定一个清理项目,它会在所有依赖该项目的项目运行完成之后执行。给 setup 项目加上teardown: 'cleanup db',并新增cleanup db项目匹配global.teardown.ts

import { 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()从每个顶层项目出发递归展开depsteardown,得到“顶级项目/依赖项目”的映射;递归深度超过 100 层时抛出Circular dependency detected between projects.,即项目间循环依赖会被显式报错(见 projectUtils.ts#L93-L116)。
  • teardown 到 setup 的反向映射buildTeardownToSetupsMap()把“哪个 teardown 服务于哪些 setup 项目”建为 Map(见 projectUtils.ts#L81-L91),teardown 项目因此也能被纳入执行计划。
  • 阶段(Phase)划分:在 tasks.ts 的createPhasesTask()中,运行器把“所有依赖均已在之前阶段处理完毕”的项目归入同一阶段,按阶段串行推进。值得注意的一个细节:当配置了maxFailures时,teardown 项目被放入独立阶段且标记为ignoreMaxFailures——即使测试失败数达到上限,清理项目依然会运行,保证 setup 产生的副作用(数据库、临时资源等)不会被跳过清理(见 tasks.ts#L374-L378)。
  • 环境变量继承:在 tasks.ts 的createRunTestsTask()中,每个项目执行时会从它的deps与关联 setup 继承额外环境变量(extraEnvByProjectId),并跳过“依赖项目中有失败项”的项目——依赖项目失败时,下游项目不会被调度,但 teardown 仍会执行。

这也解释了为什么推荐用项目依赖:setup/teardown 是普通测试,因此天然拥有 fixture、重试、trace 和报告可见性。

三、方案二:globalSetupglobalTeardown配置项

globalSetup选项用于在所有测试运行前设置一次性的环境。setup 文件必须导出一个单一函数,该函数接收一个 config 对象(FullConfig类型),只运行一次。类型定义见 test.d.ts#L1317-L1358:两个选项都接受string | Array<string>,即支持传入多个 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 示例:登录一次并复用认证状态

下面的示例使用baseURLstorageState配置项,在 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;

在配置文件中指定globalSetupbaseURLstorageState

import { 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_TEST=1环境变量,这在 globalSetup and globalTeardown should have PLAYWRIGHT_TEST=1 用例中被显式验证。

3.3 捕获 global setup 失败时的 trace

globalSetup方案本身不在报告中展示(见对比表),但仍可手动录制 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 failures
global setup 超时时 teardown 仍会运行globalTeardown still runs when globalSetup times out
setup 出错会导致测试完全不执行globalSetup error should prevent tests from executing
多文件时:setup 按声明顺序执行;setup 返回的清理回调按声明的反序执行;teardown 文件按声明顺序执行globalSetup should support multiple
setup 回调失败不影响globalTeardown执行globalTeardown runs even if callback failed
支持从node_modules引用(如globalSetup: 'my-global-setup'globalSetup should allow requiring a package from node_modules

这些用例共同确认了globalSetup方案的失败语义:setup 失败会阻止测试执行,而 teardown 在绝大多数路径(测试失败、setup 超时、回调报错)下都会被执行,与“清理必须发生”的设计意图一致。

四、方案选择建议

综合原文档对比表与源码实现,选型可以遵循以下原则:

  1. 默认优先项目依赖:setup/teardown 是普通测试,自动获得 fixture、trace、报告可见性与重试能力;headlesstestIdAttribute等项目配置项自动生效;依赖失败会正确阻止下游项目运行,且maxFailures不会跳过 teardown 清理。
  2. globalSetup适合轻量、无浏览器场景:如设置环境变量、启动一次性服务、生成临时资源。若需要浏览器操作(如登录),注意所有配置项需手动从FullConfig读取,且需自行处理 trace(见 3.3 节的 try...catch 模式)。
  3. 数据传递globalSetup方案用process.env向测试传值(JSON 字符串化复杂结构);项目依赖方案则可用 fixture 与配置项(如storageState)获得更类型安全的传递方式。
  4. 更完整的实战示例可参考仓库中的 认证指南,其中演示了用项目依赖复用登录态的完整写法;官方文档同时提及了“利用项目依赖复用登录”的官方博客文章与 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询