Skip to content

Playwright Fixtures 与参数化

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


什么是 Fixture

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

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

实例

javascript

// 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。

实例

bash

// 文件路径: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

实例

javascript

// 文件路径: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 内所有测试共享

实例

javascript

// 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 来改变默认行为。

实例

javascript

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 的效果。

实例

javascript

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 没有内置的参数化机制,但可以通过循环动态生成测试。

实例

javascript

// 数据驱动测试

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)

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

实例

javascript

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" 只运行此测试

});

AI 思考中...

Playwright 页面对象模型(POM)

Playwright 并行、分片与重试

基于 VitePress 构建,部署于 GitHub Pages