测试夹具 Fixtures 详解
本教程共 59 篇 · 第 37 篇 · 更新于 2026-08-04 · 约 11 分钟阅读
37. 测试夹具 Fixtures 详解
本节目标:学完能说清 Fixture(夹具)是什么,为什么它比 before/after 钩子更优雅,以及它是怎么做到按需、隔离、可组合的。
你写的第一个测试里其实就用过夹具了——{ page } 那个参数就是。本章把它讲透:Fixture(夹具)到底是什么,凭什么成为 Playwright 测试里最重要的概念。
夹具解决什么问题
测试前总要准备环境:打开页面、登录、造数据。传统写法靠 beforeEach 一把梭,把所有准备都堆进去。问题是,有些测试根本不需要其中某些准备,却照样被拖着跑一遍。
夹具的思路不同:每个测试只声明自己要什么,测试运行器就只准备什么。需要 page 就给 page,需要别的再说。
NoteFixture(夹具)是 Playwright Test 的核心概念,用来为每个测试搭建「刚好够用」的运行环境,并且测试之间彼此隔离。
一个最简单的例子
你早见过了:
import { test, expect } from '@playwright/test';
test('基础测试', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
});
那个 { page } 就是在告诉运行器:「我要一个 page 夹具」。运行器负责把它准备好再传进来。
相比钩子的四个优势
用夹具代替 before/after 钩子,好处很明显:
- 封装:准备和清理写在同一个地方,一目了然。
- 复用:定义一次,所有测试文件都能用,内置的
page就是这么来的。 - 按需:只有测试真正用到的夹具才会被初始化。
- 组合:夹具之间能互相依赖,拼出复杂行为。
Tip如果你有一堆测试都在
beforeEach里建同一个东西、又在afterEach里销毁,那它八成该升级成夹具。
惰性初始化
这是夹具最反直觉也最省心的一点。假设你定义了十个夹具,一条测试只用其中两个,Playwright 就只初始化那两个,其余八个碰都不碰。
对比钩子:钩子一旦写进 beforeEach,每个测试都得跑。夹具则是「用到才建」,测试数量一多,差距就出来了。
依赖注入怎么玩
夹具之间可以依赖。比如 todoPage 夹具内部依赖 page:
import { test as base } from '@playwright/test';
import { TodoPage } from './todo-page'; // 你自己写的页面封装类
const test = base.extend<{ todoPage: TodoPage }>({
todoPage: async ({ page }, use) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo('item1');
await use(todoPage); // 把实例交给测试
await todoPage.removeAll(); // 测试结束后清理
},
});
test('能新增一项', async ({ todoPage }) => {
await todoPage.addToDo('my item');
});
注意 use(todoPage) 这个调用:它之前是准备阶段,之后是清理阶段。测试代码就夹在中间跑。
执行顺序有规矩
夹具的 setup 和 teardown 围绕 use() 展开,遵循几条规则:
- A 依赖 B,则 B 先建、A 后建;结束时反过来,A 先拆、B 后拆。
- 非自动夹具是惰性的,用到才建。
- 测试级夹具每个测试后拆;worker(工作进程)级夹具只在进程结束时拆一次。
Warning别在夹具里放「测试之间要传递的状态」。每个测试的夹具实例都是全新的,靠它传值会翻车。
自动夹具与覆盖
用元组语法加 auto: true,可以让夹具对每一条测试自动生效,无需声明。还能用 scope: 'worker' 让它在整个 worker 进程里只建一次。这些进阶玩法放到第 39 章细说,下一章先把内置夹具认全。
什么时候该上夹具
我的判断很简单:凡是「多个测试都要、且需要清理」的准备工作,都值得做成夹具。它让测试文件读起来像在讲业务,而不是在一堆准备代码里找重点。
小结
夹具就是 Playwright 给测试准备的「按需自助」——你声明要什么,它就给你准备好,用完自动收拾。核心记住三点:依赖注入让夹具能互相组合、惰性初始化省掉了不必要的开销、use() 前后分别是 setup 和 teardown。以后看见 beforeEach 堆一堆准备代码,就该想想能不能提成夹具了。