现在位置: 首页 > Playwright 教程 > 正文

Playwright Fixtures 与参数化

本章介绍 Playwright 的 Fixture 机制、如何创建自定义 Fixture 以及参数化测试的技巧。


什么是 Fixture

Fixture(夹具)是 Playwright 测试函数参数的依赖注入机制。

每个测试通过解构参数的方式获取所需的 Fixture(如 { page }),Playwright 负责创建和清理。

实例

// page 和 context 是内置 Fixture
test('使用内置 Fixture', async ({ page, context }) => {
  // Playwright 自动创建 page 和 context
  // 测试结束后自动清理
  await page.goto('https://www.runoob.com/');
});

内置 Fixture 速查

Fixture类型说明
pagePage独立的浏览器标签页
contextBrowserContext浏览器上下文
browserBrowser浏览器实例
browserNamestring当前浏览器名称
requestAPIRequestContextHTTP 请求客户端

自定义 Fixture

通过 test.extend() 创建自定义 Fixture,可以注入额外的依赖或覆盖内置 Fixture。

实例

// 文件路径:tests/fixtures.ts
import { test as base, expect } from '@playwright/test';

// 定义自定义 Fixture 的类型
type MyFixtures = {
  todoPage: { goto: () => Promise<void>; addTodo: (text: string) => Promise<void> };
  authenticatedPage: any;
};

export const test = base.extend<MyFixtures>({
  // 自定义 Fixture:Todo 页面对象
  todoPage: async ({ page }, use) => {
    // 创建
    const todoPage = {
      goto: async () => {
        await page.goto('https://demo.playwright.dev/todomvc');
      },
      addTodo: async (text: string) => {
        await page.getByPlaceholder('What needs to be done?').fill(text);
        await page.keyboard.press('Enter');
      },
    };
    // 注入到测试中
    await use(todoPage);
    // 清理逻辑(如果需要)
  },

  // 自定义 Fixture:已认证的页面
  authenticatedPage: async ({ browser }, use) => {
    const context = await browser.newContext({
      storageState: 'playwright/.auth/user.json',
    });
    const page = await context.newPage();
    await use(page);
    await context.close();
  },
});

export { expect };

使用自定义 Fixture

实例

// 文件路径:tests/todo.spec.ts
import { test, expect } from './fixtures';

test('添加 Todo', async ({ todoPage }) => {
  await todoPage.goto();
  await todoPage.addTodo('学习 Playwright');
  await todoPage.addTodo('掌握 Fixtures');
  // 验证...
});

Fixture 的作用域

Fixture 有三种作用域,通过 scope 选项控制。

作用域生命周期
测试级(默认)scope: 'test'每个测试创建一次
Worker 级scope: 'worker'每个 Worker 进程创建一次,同一 Worker 内所有测试共享

实例

// Worker 级 Fixture(同一 Worker 内所有测试共享)
const test = base.extend({
  sharedResource: [async ({}, use) => {
    const resource = await createExpensiveResource();
    await use(resource);
    await resource.dispose();
  }, { scope: 'worker' }],  // 注意数组语法
});

Worker 级 Fixture 在同一 Worker 进程内的多个测试间共享,适合数据库连接等创建开销大的资源。


覆盖内置 Fixture

你可以覆盖内置 Fixture 来改变默认行为。

实例

const test = base.extend({
  // 覆盖内置的 page fixture,改变默认视口
  page: async ({ baseURL, page }, use) => {
    await page.setViewportSize({ width: 1920, height: 1080 });
    await use(page);
  },

  // 覆盖 storageState
  storageState: 'playwright/.auth/admin.json',
});

Fixture 自动清理

Fixture 的清理逻辑在 use() 调用之后执行,类似于 afterEach 的效果。

实例

const test = base.extend({
  tempUser: async ({ request }, use) => {
    // 创建临时用户
    const resp = await request.post('/api/users', {
      data: { name: 'temp_' + Date.now() },
    });
    const user = await resp.json();

    // 注入到测试
    await use(user);

    // 自动清理:删除临时用户
    await request.delete(`/api/users/${user.id}`);
  },
});

参数化测试

Playwright 没有内置的参数化机制,但可以通过循环动态生成测试。

实例

// 数据驱动测试
const testCases = [
  { username: 'admin', password: 'admin123', expected: '仪表盘' },
  { username: 'user',  password: 'user123',  expected: '个人中心' },
  { username: 'guest', password: 'guest123', expected: '访客页面' },
];

for (const { username, password, expected } of testCases) {
  test(`用户 ${username} 登录后看到 ${expected}`, async ({ page }) => {
    await page.goto('/login');
    await page.getByLabel('用户名').fill(username);
    await page.getByLabel('密码').fill(password);
    await page.getByRole('button', { name: '登录' }).click();
    await expect(page.getByText(expected)).toBeVisible();
  });
}

测试注解(Annotations)

注解用于给测试添加元数据,在报告器中可以查看和过滤。

实例

test('带有注解的测试', async ({ page }) => {
  // 添加注解(在测试函数体内)
  test.info().annotations.push({
    type: 'issue',
    description: 'https://github.com/runoob/bug/123',
  });
  test.info().annotations.push({
    type: 'description',
    description: '这个测试覆盖了登录流程的所有分支',
  });

  // 测试逻辑...
});

// 使用 Tag(在测试名称中)
test('快速冒烟测试 @smoke @critical', async ({ page }) => {
  // 可通过 npx playwright test --grep "@smoke" 只运行此测试
});