首页 / Playwright 入门教程 / 参数化测试 parallel 与 step

Playwright 入门教程

参数化测试 parallel 与 step

本教程共 59 篇 · 第 42 篇 · 更新于 2026-08-04 · 约 10 分钟阅读

Playwright参数化paralleltest.stepdescribe.configure步骤

42. 参数化测试 parallel 与 step

本节目标:学完能用一套代码跑多组数据(参数化),让单文件内测试并行,并用 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

并行测试之间不能共享状态或全局变量。它们各自跑全套钩子,包括 beforeAllafterAll。依赖顺序的测试别标并行。

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 也会跟着重来。