Playwright 高级配置 #

配置文件结构 #

完整配置示例 #

typescript
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  // 全局配置
  testDir: './tests',
  testMatch: /.*\.spec\.ts/,
  testIgnore: /.*\.skip\.ts/,
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  timeout: 30000,
  globalTimeout: 60000,
  
  // 报告器配置
  reporter: [
    ['list'],
    ['html', { outputFolder: 'playwright-report', open: 'never' }],
  ],
  
  // 全局设置
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
  
  // expect 配置
  expect: {
    timeout: 5000,
    toHaveScreenshot: {
      maxDiffPixels: 100,
    },
  },
  
  // use 配置
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    headless: true,
    viewport: { width: 1280, height: 720 },
    ignoreHTTPSErrors: true,
    actionTimeout: 10000,
    navigationTimeout: 30000,
  },
  
  // 项目配置
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
    },
    {
      name: 'Mobile Safari',
      use: { ...devices['iPhone 12'] },
    },
  ],
  
  // 开发服务器
  webServer: {
    command: 'npm run start',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
    timeout: 120000,
  },
});

测试运行器配置 #

测试发现 #

typescript
export default defineConfig({
  // 测试目录
  testDir: './tests',
  
  // 多个目录
  testDir: ['./tests', './e2e'],
  
  // 测试文件匹配模式
  testMatch: /.*\.spec\.ts/,
  testMatch: ['**/*.spec.ts', '**/*.test.ts'],
  
  // 忽略的测试文件
  testIgnore: /.*\.skip\.ts/,
  testIgnore: ['**/node_modules/**', '**/dist/**'],
});

并行执行 #

typescript
export default defineConfig({
  // 完全并行
  fullyParallel: true,
  
  // Worker 数量
  workers: 4,                    // 固定数量
  workers: process.env.CI ? 1 : undefined,  // CI 单线程
  workers: '50%',                // CPU 核心数的 50%
  
  // 项目并行
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
  ],
});

重试配置 #

typescript
export default defineConfig({
  // 全局重试
  retries: 2,
  
  // CI 环境重试
  retries: process.env.CI ? 2 : 0,
  
  // 特定项目重试
  projects: [
    {
      name: 'chromium',
      retries: 2,
      use: { ...devices['Desktop Chrome'] },
    },
  ],
});

超时配置 #

typescript
export default defineConfig({
  // 测试超时
  timeout: 30000,
  
  // 全局超时
  globalTimeout: 600000,
  
  // expect 超时
  expect: {
    timeout: 5000,
  },
  
  // 操作超时
  use: {
    actionTimeout: 10000,
    navigationTimeout: 30000,
  },
});

浏览器配置 #

设备模拟 #

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

export default defineConfig({
  projects: [
    // 桌面浏览器
    {
      name: 'Desktop Chrome',
      use: {
        ...devices['Desktop Chrome'],
        viewport: { width: 1920, height: 1080 },
      },
    },
    {
      name: 'Desktop Firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'Desktop Safari',
      use: { ...devices['Desktop Safari'] },
    },
    
    // 移动设备
    {
      name: 'iPhone 12',
      use: { ...devices['iPhone 12'] },
    },
    {
      name: 'iPhone 12 Pro',
      use: { ...devices['iPhone 12 Pro'] },
    },
    {
      name: 'iPhone 13',
      use: { ...devices['iPhone 13'] },
    },
    {
      name: 'Pixel 5',
      use: { ...devices['Pixel 5'] },
    },
    {
      name: 'Galaxy S5',
      use: { ...devices['Galaxy S5'] },
    },
    
    // 平板设备
    {
      name: 'iPad Pro',
      use: { ...devices['iPad Pro'] },
    },
    {
      name: 'Galaxy Tab S4',
      use: { ...devices['Galaxy Tab S4'] },
    },
  ],
});

自定义浏览器配置 #

typescript
export default defineConfig({
  projects: [
    {
      name: 'Chrome',
      use: {
        // 使用安装的 Chrome
        channel: 'chrome',
        viewport: { width: 1920, height: 1080 },
      },
    },
    {
      name: 'Chrome Beta',
      use: {
        channel: 'chrome-beta',
      },
    },
    {
      name: 'Chrome Dev',
      use: {
        channel: 'chrome-dev',
      },
    },
    {
      name: 'Microsoft Edge',
      use: {
        channel: 'msedge',
      },
    },
    {
      name: 'Microsoft Edge Beta',
      use: {
        channel: 'msedge-beta',
      },
    },
  ],
});

浏览器启动选项 #

typescript
export default defineConfig({
  use: {
    // 无头模式
    headless: true,
    
    // 慢动作
    launchOptions: {
      slowMo: 100,
    },
    
    // 开发者工具
    launchOptions: {
      devtools: true,
    },
    
    // 忽略 HTTPS 错误
    ignoreHTTPSErrors: true,
    
    // 下载行为
    downloadsPath: './downloads',
    acceptDownloads: true,
  },
});

上下文配置 #

视口和窗口 #

typescript
export default defineConfig({
  use: {
    // 视口大小
    viewport: { width: 1280, height: 720 },
    
    // 设备像素比
    deviceScaleFactor: 2,
    
    // 是否有触摸支持
    hasTouch: true,
    
    // 是否移动端
    isMobile: true,
    
    // 是否支持 JavaScript
    javaScriptEnabled: true,
  },
});

语言和区域 #

typescript
export default defineConfig({
  use: {
    // 语言
    locale: 'zh-CN',
    
    // 时区
    timezoneId: 'Asia/Shanghai',
    
    // 地理位置
    geolocation: { 
      latitude: 39.9042, 
      longitude: 116.4074 
    },
    
    // 权限
    permissions: ['geolocation', 'notifications'],
    
    // 用户代理
    userAgent: 'Custom User Agent',
    
    // 颜色方案
    colorScheme: 'dark', // 'light' | 'dark' | 'no-preference'
    
    // 减少动画
    reducedMotion: 'reduce',
  },
});

存储状态 #

typescript
export default defineConfig({
  use: {
    // 加载存储状态(cookies, localStorage)
    storageState: 'auth.json',
    
    // 或使用状态文件
    storageState: {
      cookies: [
        { name: 'session', value: 'abc123', domain: 'example.com', path: '/' },
      ],
      origins: [
        {
          origin: 'https://example.com',
          localStorage: [
            { name: 'token', value: 'xyz789' },
          ],
        },
      ],
    },
  },
});

报告器配置 #

多报告器 #

typescript
export default defineConfig({
  reporter: [
    // 控制台列表
    ['list'],
    
    // 详细控制台
    ['line'],
    
    // 点状
    ['dot'],
    
    // HTML 报告
    ['html', {
      outputFolder: 'playwright-report',
      open: 'never', // 'always' | 'never' | 'on-failure'
      host: 'localhost',
      port: 9323,
    }],
    
    // JSON 报告
    ['json', {
      outputFile: 'results.json',
    }],
    
    // JUnit 报告
    ['junit', {
      outputFile: 'junit.xml',
      stripANSIControlSequences: true,
    }],
    
    // GitHub Actions
    ['github'],
  ],
});

自定义报告器 #

typescript
// my-reporter.ts
import { Reporter, TestCase, TestResult } from '@playwright/test/reporter';

class MyReporter implements Reporter {
  onBegin(config, suite) {
    console.log(`开始运行 ${suite.allTests().length} 个测试`);
  }
  
  onTestBegin(test: TestCase) {
    console.log(`开始测试: ${test.title}`);
  }
  
  onTestEnd(test: TestCase, result: TestResult) {
    console.log(`测试结束: ${test.title} - ${result.status}`);
  }
  
  onEnd(result) {
    console.log(`测试完成: ${result.status}`);
  }
}

export default MyReporter;

// playwright.config.ts
export default defineConfig({
  reporter: [['./my-reporter.ts']],
});

全局设置 #

globalSetup 和 globalTeardown #

typescript
// global-setup.ts
import { FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  console.log('全局设置开始');
  
  // 启动测试服务器
  // 初始化数据库
  // 创建测试数据
  
  // 保存认证状态
  const { chromium } = require('@playwright');
  const browser = await chromium.launch();
  const page = await browser.newPage();
  
  await page.goto('https://example.com/login');
  await page.fill('#email', 'test@example.com');
  await page.fill('#password', 'password');
  await page.click('button[type="submit"]');
  
  await page.context().storageState({ path: 'auth.json' });
  await browser.close();
  
  console.log('全局设置完成');
}

export default globalSetup;

// global-teardown.ts
async function globalTeardown() {
  console.log('全局清理开始');
  // 清理测试数据
  // 关闭服务器
  console.log('全局清理完成');
}

export default globalTeardown;

// playwright.config.ts
export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  globalTeardown: require.resolve('./global-teardown'),
});

项目配置 #

项目依赖 #

typescript
export default defineConfig({
  projects: [
    // 认证设置
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
    },
    
    // 依赖认证的测试
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['setup'],
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
      dependencies: ['setup'],
    },
  ],
});

项目特定配置 #

typescript
export default defineConfig({
  projects: [
    {
      name: 'smoke',
      testMatch: /.*smoke\.spec\.ts/,
      retries: 0,
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'regression',
      testMatch: /.*regression\.spec\.ts/,
      retries: 2,
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'api',
      testDir: './api-tests',
      use: {
        baseURL: 'https://api.example.com',
      },
    },
  ],
});

命令行选项 #

运行特定项目 #

bash
# 运行特定项目
npx playwright test --project=chromium

# 运行多个项目
npx playwright test --project=chromium --project=firefox

运行特定测试 #

bash
# 运行特定文件
npx playwright test login.spec.ts

# 运行特定目录
npx playwright test tests/auth/

# 运行匹配的测试
npx playwright test -g "login"

# 使用正则
npx playwright test -g "login|logout"

其他选项 #

bash
# 有头模式
npx playwright test --headed

# 调试模式
npx playwright test --debug

# UI 模式
npx playwright test --ui

# 指定 workers
npx playwright test --workers=4

# 最大失败数
npx playwright test --max-failures=3

# 重试
npx playwright test --retries=2

# 报告器
npx playwright test --reporter=html

# 生成追踪
npx playwright test --trace on

# 时间戳
npx playwright test --timestamp

环境变量 #

配置中使用环境变量 #

typescript
export default defineConfig({
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
  },
  
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
});

测试中使用环境变量 #

typescript
test('使用环境变量', async ({ page }) => {
  const username = process.env.TEST_USERNAME;
  const password = process.env.TEST_PASSWORD;
  
  await page.fill('#username', username);
  await page.fill('#password', password);
});

最佳实践 #

1. 分离配置 #

typescript
// config/base.ts
import { defineConfig, devices } from '@playwright/test';

export const baseConfig = {
  testDir: './tests',
  timeout: 30000,
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
  },
};

// playwright.config.ts
import { defineConfig } from '@playwright/test';
import { baseConfig } from './config/base';

export default defineConfig({
  ...baseConfig,
  projects: [
    // 项目配置
  ],
});

2. 使用环境区分 #

typescript
const isCI = !!process.env.CI;
const isDev = process.env.NODE_ENV === 'development';

export default defineConfig({
  retries: isCI ? 2 : 0,
  workers: isCI ? 1 : '50%',
  reporter: isCI ? [['github'], ['html']] : [['list']],
});

3. 合理设置超时 #

typescript
export default defineConfig({
  // 测试超时应该大于所有操作超时之和
  timeout: 30000,
  
  expect: {
    // 断言超时
    timeout: 5000,
  },
  
  use: {
    // 单个操作超时
    actionTimeout: 10000,
    // 导航超时
    navigationTimeout: 30000,
  },
});

下一步 #

现在你已经掌握了高级配置,接下来学习 调试技巧 了解如何调试测试!

最后更新:2026-03-28