参数化测试 parallel 与 step
本教程共 59 篇 · 第 42 篇 · 更新于 2026-08-04 · 约 10 分钟阅读
本节目标:学完能用一套代码跑多组数据(参数化),让单文件内测试并行,并用 test.step 把冗长测试拆成可读的步骤。
写测试常遇到这种情况:同一个流程,要拿五六个不同输入各跑一遍。总不能复制粘贴六份吧?本章讲三个让测试更紧凑、更清晰的手段。
参数化:一套代码多组输入
最简单的方式是用循环,把每组数据喂给同一个测试模板:
import { test, expect } from '@playwright/test';
[
{ name: 'Alice', expected: 'Hello, Alice!' },
{ name: 'Bob', expected: 'Hello, Bob!' },
{ name: 'Charlie', expected: 'Hello, Charlie!' },
].forEach(({ name, expected }) => {
test(`问候 ${name}`, async ({ page }) => {
await page.goto(`https://example.com/greet?name=${name}`);
await expect(page.getByRole('heading')).toHaveText(expected);
});
});
这样写一份逻辑,自动生成三条独立测试,报告里各算各的。改一处,全组生效。
Note参数化(parameterize)指用数据驱动测试。每组数据生成一条独立测试用例,互不牵连。
钩子放在循环外
多数情况下,beforeEach 等钩子要写在循环外面,保证只声明一次:
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ page }) => { /* 公共准备 */ });
[
{ name: 'Alice', expected: 'Hello, Alice!' },
{ name: 'Bob', expected: 'Hello, Bob!' },
].forEach(({ name, expected }) => {
test(`问候 ${name}`, async ({ page }) => {
await page.goto(`https://example.com/greet?name=${name}`);
await expect(page.getByRole('heading')).toHaveText(expected);
});
});
如果你想要每个数据组各自独立的 beforeEach,就把钩子塞进 describe 再放进循环里。
单文件内并行
默认情况下,同一文件的测试是一条接一条跑的。想让它们并行,用 test.describe.configure:
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('并行 1', async ({ page }) => { /* ... */ });
test('并行 2', async ({ page }) => { /* ... */ });
想让所有项目里所有文件都并行,在配置里设 fullyParallel: true 更省事。注意:并行测试跑在不同 worker(工作进程)里,不能共享变量。
Warning并行测试之间不能共享状态或全局变量。它们各自跑全套钩子,包括
beforeAll和afterAll。依赖顺序的测试别标并行。
serial 串行模式
少数测试彼此依赖,必须按顺序。可以用 serial 模式:一旦其中一条失败,后续全部跳过。
import { test } from '@playwright/test';
test.describe.configure({ mode: 'serial' });
Tip官方并不推荐 serial。更好的做法是让每条测试都独立,这样无论怎么排、怎么并行都不会挂。
test.step 拆解长测试
测试一长,报告里就一团乱麻。用 test.step 把流程切成命名步骤,在报告里看得清清楚楚:
import { test, expect } from '@playwright/test';
test('完整下单流程', async ({ page }) => {
await test.step('打开商品页', async () => {
await page.goto('/product/1');
});
await test.step('加入购物车', async () => {
await page.getByRole('button', { name: 'Add to cart' }).click();
});
await test.step('提交订单', async () => {
await page.getByRole('button', { name: 'Checkout' }).click();
await expect(page.getByText('下单成功')).toBeVisible();
});
});
test.step 还能返回值,方便把上一步结果传给下一步:
import { test, expect } from '@playwright/test';
test('拿到上一步的结果', async ({ page }) => {
const user = await test.step('登录并拿到用户名', async () => {
// ...
return 'john';
});
expect(user).toBe('john');
});
步骤还能嵌套,把大的阶段拆成小步骤。步骤名会原样出现在 HTML 报告和 Trace(追踪)里,起名时写清「这一步在干嘛」,回头排错省一半时间。
三者怎么配合
我用它们解决不同问题:参数化消灭重复用例,parallel 提速,step 提升可读性。一个常见组合是——参数化出多组测试,每组内部用 step 拆步骤,整个文件设成 parallel 一起跑。
Note想按「项目维度」做参数化(同一套测试跑在不同配置上),那是
projects加选项夹具的玩法,第 39 章的option: true就是干这个的。
小结
三个工具各管一摊:参数化用循环喂多组数据,一套逻辑自动生成多条用例;describe.configure({ mode: 'parallel' }) 让文件内测试并发跑,提速但别共享状态;test.step 把长流程切成命名步骤,报告里一目了然。记住并行测试各自独立跑,beforeAll/afterAll 也会跟着重来。