Skip to content

Playwright Test

Playwright 除了作为浏览器自动化工具,还自带了一个 完整的测试框架 —— Playwright Test

Playwright Test 可以帮助我们组织测试用例、生成报告、并行执行、失败重试,功能完备,开箱即用。

  1. Playwright Test 是一个 集成的测试框架,无需额外依赖。
  2. 配置文件可设置 超时、重试、报告、浏览器项目等。
  3. 测试用例支持 分组、钩子、数据驱动
  4. 提供 并行执行、HTML 报告、失败重试 等高级能力。

测试框架概述

  • 集成测试框架:自带测试运行器,无需额外安装 Jest、Mocha。
  • 内置断言库:支持 expect 断言。
  • 测试隔离:每个 test 默认在新的浏览器上下文中执行,互不干扰。
  • 丰富功能:并行执行、重试、截图、录制、HTML 报告等。

安装方式:

bash
npm init playwright@latest

自动生成项目结构,包含配置文件 playwright.config.js

1. 配置文件详解

Playwright Test 使用 playwright.config.js(或 .ts)作为配置入口。

bash
// playwright.config.js
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',         // 测试目录
  timeout: 30 * 1000,         // 单个测试超时时间
  retries: 2,                 // 失败重试次数
  reporter: [['html'], ['list']], // 测试报告类型
  use: {
    headless: true,           // 是否无头模式
    screenshot: 'only-on-failure', // 失败时截图
    video: 'retain-on-failure',    // 失败时保留视频
    baseURL: 'https://example.com', // 基础 URL
  },
  projects: [
    { name: 'Chromium', use: { browserName: 'chromium' } },
    { name: 'Firefox',  use: { browserName: 'firefox' } },
    { name: 'WebKit',   use: { browserName: 'webkit' } },
  ],
});

2. 测试脚本结构

Playwright Test 脚本通常包含:

1、导入测试库

javascript
  import { test, expect } from '@playwright/test';

2、编写测试用例

javascript
test('示例测试', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle('Example Domain');
});

3、分组与钩子

  • 使用 describe 组织测试
  • 使用 beforeEach / afterEach 做前后置处理

编写测试用例

1. test() 函数使用

javascript
test('页面标题验证', async ({ page }) => {
  await page.goto('https://playwright.dev');
  await expect(page).toHaveTitle(/Playwright/);
});

2. describe() 分组

javascript
test.describe('用户登录模块', () => {
  test('输入正确账号密码', async ({ page }) => {
    // 登录逻辑...
  });

  test('输入错误密码', async ({ page }) => {
    // 验证错误提示...
  });
});

3. 测试钩子

javascript
test.describe('购物车模块', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('https://example.com/cart');
  });

  test.afterEach(async ({ page }) => {
    await page.screenshot({ path: 'cart.png' });
  });

  test('添加商品', async ({ page }) => {
    await page.click('#add-item');
    await expect(page.locator('#cart-count')).toHaveText('1');
  });
});

4. 测试数据准备

支持参数化测试:

javascript
const users = [
  { name: 'admin', password: '123456' },
  { name: 'guest', password: 'guest' },
];

for (const user of users) {
  test(`用户 ${user.name} 登录`, async ({ page }) => {
    await page.goto('https://example.com/login');
    await page.fill('#username', user.name);
    await page.fill('#password', user.password);
    await page.click('#submit');
    await expect(page.locator('.welcome')).toBeVisible();
  });
}

运行和报告

1. 测试执行命令

bash
npx playwright test          # 运行全部测试
npx playwright test login.spec.js   # 运行单个文件
npx playwright test -g "登录"       # 运行指定用例

2. 并行测试

Playwright 默认 按文件并行执行,也可在配置文件中指定 workers 数量:

bash
npx playwright test --workers=4

3. 测试报告生成

运行后生成 HTML 报告

bash
npx playwright show-report

4. 失败重试机制

在配置文件 playwright.config.js 中设置:

typescript
retries: 2

测试失败时会自动重试,适合处理偶发性错误。

  1. Playwright Test 是一个 集成的测试框架,无需额外依赖。
  2. 配置文件可设置 超时、重试、报告、浏览器项目等。
  3. 测试用例支持 分组、钩子、数据驱动
  4. 提供 并行执行、HTML 报告、失败重试 等高级能力。

完整测试套件示例

1、项目结构

typescript
playwright-demo/
├─ playwright.config.ts           # 全局配置(多项目、多浏览器、报告、重试…)
├─ package.json
├─ tests/
│  ├─ auth.setup.ts               # 一次性登录,生成 storageState
│  ├─ smoke/
│  │  └─ home.spec.ts             # 冒烟用例:标题/URL/截图对比
│  └─ e2e/
│     └─ cart.spec.ts             # 端到端:添加购物车、断言、附件、步骤
├─ fixtures/
│  ├─ pages.ts                    # 页面对象(PO
│  └─ test.extend.ts              # 自定义 fixtures
└─ snapshots/                     # 视觉基线(自动生成)

2、配置文件(playwright.config.ts)

实例

bash
// playwright.config.ts

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({

  testDir: './tests',

  timeout: 30_000,

  retries: 2,

  workers: 4,

  reporter: [['html', { outputFolder: 'report' }], ['list']],

  forbidOnly: !!process.env.CI,

  // 统一默认使用(可在项目或测试级覆盖)

  use: {

    baseURL: 'https://demo.playwright.dev',

    headless: true,

    screenshot: 'only-on-failure',

    video: 'retain-on-failure',

    trace: 'retain-on-failure',

    viewport: { width: 1366, height: 900 },

    locale: 'zh-CN',

  },

  // 多项目:不同浏览器/设备/登录态

  projects: [

    { name: 'Chromium', use: { ...devices['Desktop Chrome'] } },

    { name: 'Firefox',  use: { ...devices['Desktop Firefox'] } },

    {

      name: 'Mobile Chrome',

      use: { ...devices['Pixel 7'], headless: true },

    },

    // 带登录态项目(复用 storageState)

    {

      name: 'Chromium Logged-in',

      use: { storageState: 'storageState.json' },

      dependencies: ['setup'], // 依赖前置项目生成登录态

    },

    // 前置"setup"项目:只跑一次生成 storageState

    {

      name: 'setup',

      testMatch: /auth\.setup\.ts/,

    },

  ],

});

**要点:**projects 让同一套用例在不同浏览器/配置下运行;use 可在全局/项目/测试三级覆盖;报告与重试在配置中统一管理。

3、夹具与页面对象

fixtures/test.extend.ts(自定义 fixture + 公共步骤封装)

实例

bash
// fixtures/test.extend.ts

import { test as base } from '@playwright/test';

import { HomePage, CartPage } from './pages';

type MyFixtures = {

  home: HomePage;

  cart: CartPage;

};

export const test = base.extend<MyFixtures>({

  home: async ({ page }, use) => {

    const home = new HomePage(page);

    await use(home);

  },

  cart: async ({ page }, use) => {

    const cart = new CartPage(page);

    await use(cart);

  },

});

export { expect } from '@playwright/test';

基于 test.extend() 增加自定义 fixture,便于在任意用例中直接注入 home、cart 实例。

fixtures/pages.ts(页面对象)

实例

bash
// fixtures/pages.ts

import { Page, expect } from '@playwright/test';

export class HomePage {

  constructor(private page: Page) {}

  async goto() { await this.page.goto('/'); }

  get tryTodoLink() { return this.page.getByRole('link', { name: /ToDo MVC/i }); }

}

export class CartPage {

  constructor(private page: Page) {}

  async goto() { await this.page.goto('/e2e'); } // 假设存在 e2e 示例页

  get addFirstItem() { return this.page.getByRole('button', { name: /Add .* #1/i }); }

  get cartCount() { return this.page.locator('[data-test=cart-count]'); }

  async addOne() { await this.addFirstItem.click(); }

  async expectCount(n: number) { await expect(this.cartCount).toHaveText(String(n)); }

}

4、前置登录(一次性生成 storageState)

tests/auth.setup.ts(仅在 "setup" 项目里执行)

实例

javascript
// tests/auth.setup.ts

import { test, expect } from '@playwright/test';

test('create storageState', async ({ page }) => {

  await page.goto('https://demo.playwright.dev/todomvc');

  // ……此处替换为你的登录流程……

  // 假设已登录成功

  await expect(page).toHaveURL(/todomvc/);

  await page.context().storageState({ path: 'storageState.json' });

});

5、冒烟测试(标题/URL/视觉对比)

tests/smoke/home.spec.ts

实例

javascript
import { test, expect } from '@playwright/test';

test.describe('Smoke - Home', () => {

  test('title & url & visual snapshot', async ({ page }) => {

    await page.goto('https://playwright.dev');

    await expect(page).toHaveTitle(/Playwright/);

    await expect(page).toHaveURL(/playwright\.dev/);

    // 视觉回归(自动生成/比对基线)

    await expect(page).toHaveScreenshot(); // 首次运行会生成基线

  });

});

视觉对比建议使用 toHaveScreenshot(优先于用 page.screenshot() + toMatchSnapshot 的手写方式)。

6、端到端测试(步骤、附件、断言)

tests/e2e/cart.spec.ts

实例

javascript
import { test, expect } from '../../fixtures/test.extend';

test.describe.configure({ mode: 'parallel' });

test.describe('E2E - Cart', () => {

  test.beforeEach(async ({ home }) => {

    await test.step('进入首页', async () => {

      await home.goto();

    });

  });

  test('添加商品到购物车(含附件与断言)', async ({ page, cart }) => {

    await test.step('进入购物页并添加商品', async () => {

      await cart.goto();

      await cart.addOne();

      await cart.expectCount(1);

    });

    await test.step('附加调试信息到报告', async () => {

      await test.info().attach('state.json', {

        contentType: 'application/json',

        body: Buffer.from(JSON.stringify({ ts: Date.now(), note: 'after add' })),

      });

    });

    // 元素级截图

    await test.step('元素截图', async () => {

      const badge = page.locator('[data-test=cart-count]');

      await expect(badge).toBeVisible();

      await expect(badge).toHaveScreenshot(); // 元素快照

    });

  });

});

用 test.step 分层记录步骤、用 test.info().attach()把调试数据/截图附加到报告,便于排查;并行模式可用 describe.configure({ mode: 'parallel' })。

7、运行与报告

常用命令:

bash
npx playwright test                # 运行全部
npx playwright test tests/e2e      # 运行目录
npx playwright test -g "Cart"      # 按标题过滤
npx playwright test --project="Chromium"   # 指定项目
npx playwright show-report         # 打开HTML报告
npx playwright test --update-snapshots     # 更新视觉基线
npx playwright test --debug        # 调试模式(Inspector)

CLI 过滤、项目选择、报告查看与快照更新等见官方 CLI/报告/快照文档。


常用相关 API 速查表(Playwright Test)

1、测试与分组

API作用
test(name, fn)定义测试
test.describe(title, fn)分组
test.describe.configure({ mode })parallel / serial
test.beforeAll/afterAll组级前后置
test.beforeEach/afterEach用例前后置
test.skip/only/fixme/slow/fail标记/控制用例

2、夹具与配置

API作用
test.extend<Fixtures>(defs)扩展自定义 fixtures
test.use(options)测试级覆盖 use 选项(如 storageStateviewportlocale 等)
defineConfig({...})配置文件入口(testDirretriesworkersreporter…)
projects多项目/多浏览器/不同配置运行
use: { baseURL, trace, video, screenshot, viewport, locale, timezoneId, storageState }运行时环境与采集策略

fixtures / use / 配置项与 projects 详见官方 "Fixtures / Use options / Configuration / Projects" 文档。(Playwright)

3、断言(expect)

API作用
expect(value).toBe / toEqual / toContain ...通用断言
expect(locator).toHaveText/Value/AttributeWeb 特有断言(带智能等待)
expect(locator).toBeVisible/Hidden/Enabled/Disabled/Checked状态断言
expect(page).toHaveTitle/URL页面断言
`expect(page/locator).toHaveScreenshot([nameoptions])`视觉快照对比(推荐)

断言与视觉快照的推荐做法详见 Assertions 与 Snapshots 文档。(Playwright)

4、步骤与附件

API作用
test.step(name, fn)将测试分解为步骤(报告可视化)
`test.info().attach(name, { pathbody, contentType })`给测试或步骤附加文件/数据(报告中展示)

附件也可作用于步骤级(参见 TestStep / TestStepInfo)。(Playwright)

5、报告与 CLI

命令/配置作用
reporter: [['html', { outputFolder }], ['list']]报告配置
npx playwright test --project="Chromium"选择项目运行
-g "keyword" / --grep / --grep-invert过滤用例
--update-snapshots更新视觉基线
npx playwright show-report打开报告

完整 CLI 与 Reporter 选项见官方文档。(Playwright)

AI 思考中...

Playwright 数据提取和验证

Playwright 网络拦截

基于 VitePress 构建,部署于 GitHub Pages