跳转至

Playwright E2E 测试 — 微软出品的跨浏览器自动化测试框架


一句话说明

Playwright 是微软开源的端到端(E2E)测试框架,用一套代码测试 Chrome、Firefox、Safari 三大浏览器,支持自动等待、网络拦截、截图比对,当前版本 v1.50+(2025年持续更新)。


安装与配置

# 初始化 Playwright 项目(推荐)
npm init playwright@latest
# 会交互式询问:语言、测试目录、是否装浏览器等

# 手动安装
npm install -D @playwright/test     # 安装测试框架
npx playwright install              # 下载 Chromium/Firefox/WebKit 浏览器二进制

# 只安装指定浏览器(节省空间)
npx playwright install chromium     # 只装 Chrome
npx playwright install --with-deps  # 同时安装系统依赖

# 查看版本
npx playwright --version
// playwright.config.ts — 配置文件
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',               // 测试文件目录
  fullyParallel: true,              // 所有测试并行执行
  retries: process.env.CI ? 2 : 0, // CI 环境失败重试2次
  workers: process.env.CI ? 1 : undefined,  // CI 单进程,本地自动

  reporter: 'html',                 // 生成 HTML 测试报告

  use: {
    baseURL: 'http://localhost:5173',  // 测试的基础 URL
    trace: 'on-first-retry',          // 失败重试时记录 trace(调试神器)
    screenshot: 'only-on-failure',    // 失败时截图
    video: 'retain-on-failure',       // 失败时保留视频
  },

  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },   // Chrome 测试
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },  // Firefox 测试
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },   // Safari 测试
    { name: 'Mobile',   use: { ...devices['iPhone 14'] } },        // 移动端测试
  ],

  // 运行测试前启动开发服务器
  webServer: {
    command: 'npm run dev',           // 启动命令
    url: 'http://localhost:5173',     // 等待这个 URL 可访问
    reuseExistingServer: !process.env.CI,  // 本地复用已有服务器
  },
});

核心用法

// tests/example.spec.ts
import { test, expect } from '@playwright/test';  // 导入测试函数

test('用户登录流程', async ({ page }) => {         // page 是浏览器页面对象
  // ── 导航 ──
  await page.goto('/login');                       // 打开登录页

  // ── 填写表单 ──
  await page.fill('[name=username]', 'testuser');  // 填写用户名
  await page.fill('[name=password]', 'password');  // 填写密码
  await page.click('button[type=submit]');          // 点击提交按钮

  // ── 断言(自动等待,无需 sleep)──
  await expect(page).toHaveURL('/dashboard');      // 应该跳转到 dashboard
  await expect(page.locator('h1')).toHaveText('欢迎回来');  // 标题应该显示欢迎

  // ── 截图(可视化回归测试)──
  await expect(page).toHaveScreenshot('dashboard.png');  // 与基准图比较
});

test('搜索功能', async ({ page }) => {
  await page.goto('/');

  // 使用角色选择器(更稳健,不依赖 CSS class)
  const searchInput = page.getByRole('searchbox', { name: '搜索基因' });
  await searchInput.fill('TP53');                  // 输入搜索词
  await searchInput.press('Enter');                // 按回车

  // 等待结果出现
  const results = page.locator('.search-result');
  await expect(results).toHaveCount(5);            // 应有5个结果
});

实战案例

// ── 测试生信工具的文件上传功能 ──
import { test, expect } from '@playwright/test';
import path from 'path';

test('上传 FASTQ 文件并运行质控', async ({ page }) => {
  await page.goto('/tools/qc');

  // ── 文件上传 ──
  const fileInput = page.locator('input[type=file]');
  await fileInput.setInputFiles(
    path.join(__dirname, 'fixtures/sample.fastq')  // 测试用的小 FASTQ 文件
  );

  // ── 等待上传完成 ──
  await expect(page.locator('.upload-success')).toBeVisible();  // 成功提示可见

  // ── 点击运行按钮 ──
  await page.click('button:has-text("运行 QC")');

  // ── 等待分析完成(可能需要较长时间)──
  await expect(page.locator('.qc-report')).toBeVisible({ timeout: 60000 });  // 最多等60秒

  // ── 验证报告内容 ──
  const totalReads = page.locator('[data-testid=total-reads]');
  await expect(totalReads).not.toHaveText('0');    // 总读段数不为0

  // ── 下载报告 ──
  const downloadPromise = page.waitForEvent('download');    // 等待下载事件
  await page.click('button:has-text("下载报告")');
  const download = await downloadPromise;
  expect(download.suggestedFilename()).toContain('.html');  // 下载的应该是 HTML 文件
});

// ── API 拦截(Mock 后端)──
test('在无后端情况下测试前端', async ({ page }) => {
  // 拦截 API 请求,返回 mock 数据
  await page.route('/api/genes', async route => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'TP53', expression: 3.2 },  // mock 基因数据
        { id: 2, name: 'BRCA1', expression: 1.5 },
      ]),
    });
  });

  await page.goto('/genes');
  await expect(page.locator('.gene-card')).toHaveCount(2);  // 应渲染2张卡片
});

常见报错与解决

报错原因解决方法
Timeout 30000ms exceeded元素未出现或操作太慢增加 timeout,或检查选择器是否正确
Error: browserType.launch浏览器二进制未安装运行 npx playwright install
截图比较失败基准图过期运行 npx playwright test --update-snapshots 更新基准
CI 中测试不稳定并发太多设置 workers: 1 或增加 retries: 2
page.fill 找不到元素选择器错误npx playwright codegen 自动生成选择器

速查表

# 运行测试
npx playwright test                    # 运行所有测试
npx playwright test tests/login.spec.ts  # 运行指定文件
npx playwright test --headed           # 有界面模式(能看到浏览器操作)
npx playwright test --debug            # 调试模式(可断点)
npx playwright test --ui               # 启动 Playwright UI 界面

# 生成代码
npx playwright codegen http://localhost:5173  # 录制操作,自动生成测试代码

# 查看报告
npx playwright show-report             # 打开上次测试的 HTML 报告

# 更新截图基准
npx playwright test --update-snapshots

# 常用选择器
page.getByRole('button', { name: '提交' })  # 按角色选择(推荐)
page.getByText('欢迎')                        # 按文本内容
page.getByTestId('submit-btn')               # 按 data-testid
page.locator('#id .class')                   # CSS 选择器(备用)

# 官方文档:https://playwright.dev/docs/intro