Files
2026-07-13 21:36:47 +08:00

18 KiB
Raw Permalink Blame History

CIGitHub Actions

使用时机:在拉取请求、合并到主分支或按计划自动运行 Playwright 测试。GitHub Actions 是 Playwright 项目中最常用的 CI。

快速参考

# CI 的关键 CLI 标志
npx playwright install --with-deps    # 安装浏览器 + 操作系统依赖
npx playwright test --shard=1/4       # 运行 4 个分片中的第 1 个
npx playwright test --reporter=github # 在 PR 上标注失败信息
npx playwright merge-reports ./blob-report  # 合并分片报告

模式

模式 1:生产就绪工作流(可直接复制的起步模板)

使用时机:任何使用 GitHub Actions 的项目。这是一个完整且经过实战检验的工作流。

# .github/workflows/playwright.yml
name: Playwright Tests

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# 取消同一 PR/分支上的进行中运行
concurrency:
  group: playwright-${{ github.ref }}
  cancel-in-progress: true

env:
  CI: true

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Cache Playwright browsers
        id: playwright-cache
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        if: steps.playwright-cache.outputs.cache-hit != 'true'
        run: npx playwright install --with-deps

      - name: Install Playwright OS dependencies
        if: steps.playwright-cache.outputs.cache-hit == 'true'
        run: npx playwright install-deps

      - name: Run Playwright tests
        run: npx playwright test

      - name: Upload HTML report
        uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 14

      - name: Upload test traces
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-traces
          path: test-results/
          retention-days: 7

模式 2:使用矩阵策略的分片执行

使用时机:测试套件耗时超过 10 分钟。分散到多个并行运行器以缩短实际耗时。 避免时机:测试套件在 5 分钟以内——分片开销(检出、安装、合并)会抵消其带来的好处。

# .github/workflows/playwright-sharded.yml
name: Playwright Tests (Sharded)

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: playwright-${{ github.ref }}
  cancel-in-progress: true

env:
  CI: true

jobs:
  test:
    timeout-minutes: 20
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1/4, 2/4, 3/4, 4/4]

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Cache Playwright browsers
        id: playwright-cache
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        if: steps.playwright-cache.outputs.cache-hit != 'true'
        run: npx playwright install --with-deps

      - name: Install Playwright OS dependencies
        if: steps.playwright-cache.outputs.cache-hit == 'true'
        run: npx playwright install-deps

      - name: Run Playwright tests (shard ${{ matrix.shard }})
        run: npx playwright test --shard=${{ matrix.shard }}

      - name: Upload blob report
        uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: blob-report-${{ strategy.job-index }}
          path: blob-report/
          retention-days: 1

  merge-reports:
    if: ${{ !cancelled() }}
    needs: test
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Download all blob reports
        uses: actions/download-artifact@v4
        with:
          path: all-blob-reports
          pattern: blob-report-*
          merge-multiple: true

      - name: Merge reports
        run: npx playwright merge-reports --reporter=html ./all-blob-reports

      - name: Upload merged HTML report
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 14

分片配置——添加 blob reporter 以便合并分片输出:

// playwright.config.ts
import { defineConfig } from "@playwright/test"

export default defineConfig({
  reporter: process.env.CI ? [["blob"], ["github"]] : [["html", { open: "on-failure" }]],
})

模式 3:可复用工作流

使用时机:多个仓库或多个工作流文件需要相同的 Playwright 配置。 避免时机:只有一个工作流的单个仓库。

# .github/workflows/playwright-reusable.yml
name: Playwright Reusable

on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: "20"
      test-command:
        type: string
        default: "npx playwright test"
      shard-total:
        type: number
        default: 1
    secrets:
      BASE_URL:
        required: false
      TEST_PASSWORD:
        required: false

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: ${{ fromJson(format('[{0}]', join(fromJson(format('[{0}]', inputs.shard-total == 1 && '"1/1"' || '"1/4","2/4","3/4","4/4"')), ','))) }}

    env:
      CI: true
      BASE_URL: ${{ secrets.BASE_URL }}
      TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          cache: "npm"

      - run: npm ci

      - name: Cache Playwright browsers
        id: playwright-cache
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        if: steps.playwright-cache.outputs.cache-hit != 'true'
        run: npx playwright install --with-deps

      - name: Install Playwright OS dependencies
        if: steps.playwright-cache.outputs.cache-hit == 'true'
        run: npx playwright install-deps

      - name: Run tests
        run: ${{ inputs.test-command }} --shard=${{ matrix.shard }}

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report-${{ strategy.job-index }}
          path: playwright-report/
          retention-days: 14

调用可复用工作流:

# .github/workflows/ci.yml
name: CI
on:
  pull_request:
    branches: [main]

jobs:
  e2e:
    uses: ./.github/workflows/playwright-reusable.yml
    with:
      node-version: "20"
      shard-total: 4
    secrets:
      BASE_URL: ${{ secrets.STAGING_URL }}
      TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

模式 4:在容器中运行

使用时机:你需要一个与本地 Docker 运行一致的可重现环境,或者运行器的操作系统依赖引起问题。 避免时机:标准的 ubuntu-latest 配合 --with-deps 已经能正常工作(这是常见情况)。

# .github/workflows/playwright-container.yml
name: Playwright (Container)

on:
  pull_request:
    branches: [main]

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v1.52.0-noble

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci

      # 无需安装浏览器——它们已在容器镜像中
      - name: Run Playwright tests
        run: npx playwright test
        env:
          HOME: /root # 在容器中以 root 身份运行时需要

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 14

模式 5:环境密钥与部署目标

使用时机:测试针对需要身份验证凭据的预发/生产环境运行。 避免时机:测试只针对本地启动的开发服务器运行。

# .github/workflows/playwright-staging.yml
name: Playwright (Staging)

on:
  push:
    branches: [main]
  workflow_dispatch:

jobs:
  test:
    timeout-minutes: 30
    runs-on: ubuntu-latest
    environment: staging # 包含保护规则的 GitHub Environment

    env:
      CI: true
      BASE_URL: ${{ vars.STAGING_URL }}
      TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
      API_KEY: ${{ secrets.API_KEY }}

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - run: npm ci

      - name: Cache Playwright browsers
        id: playwright-cache
        uses: actions/cache@v4
        with:
          path: ~/.cache/ms-playwright
          key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

      - name: Install Playwright browsers
        if: steps.playwright-cache.outputs.cache-hit != 'true'
        run: npx playwright install --with-deps

      - name: Install Playwright OS dependencies
        if: steps.playwright-cache.outputs.cache-hit == 'true'
        run: npx playwright install-deps

      - name: Run tests against staging
        run: npx playwright test --grep @smoke

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: staging-report
          path: playwright-report/
          retention-days: 14

模式 6:定时运行(夜间回归)

使用时机:完整回归套件对每个 PR 来说太慢。改为每晚针对主分支运行一次。 避免时机:测试套件在 15 分钟内跑完,且可以在每个 PR 上运行。

# .github/workflows/playwright-nightly.yml
name: Nightly Regression

on:
  schedule:
    - cron: "0 3 * * 1-5" # UTC 时间凌晨 3 点,周一至周五
  workflow_dispatch: # 允许手动触发

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest

    env:
      CI: true
      BASE_URL: ${{ vars.STAGING_URL }}

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "npm"

      - run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps

      - name: Run full regression suite
        run: npx playwright test --grep @regression

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: nightly-report-${{ github.run_number }}
          path: playwright-report/
          retention-days: 30

      - name: Notify on failure
        if: failure()
        uses: slackapi/slack-github-action@v1.27.0
        with:
          payload: |
            {
              "text": "Nightly Playwright regression failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

决策指南

场景 做法 原因
小型套件(< 5 分钟) 单个 job,不分片 分片开销超过节省的时间
中型套件(5-20 分钟) 使用矩阵策略分为 2-4 个分片 将实际耗时缩短约 60-75%
大型套件(20+ 分钟) 4-8 个分片 + blob report 合并 将 PR 反馈时间控制在 10 分钟以内
PR 上的跨浏览器测试 PR 上仅 Chromium;主分支上测试所有浏览器 PR 上的耗时减少 3 倍
预发/生产冒烟测试 使用 environment: 的独立工作流 隔离密钥,添加审批关卡
夜间完整回归测试 schedule 触发器 + workflow_dispatch 全面覆盖,不阻塞 PR
多个仓库,相同配置 使用 workflow_call 的可复用工作流 DRY 原则;更新一个文件,所有仓库受益
需要可重现环境 使用 Playwright 镜像的容器 job 与本地 Docker 环境完全一致

反模式

反模式 问题 应改为
没有 concurrency 分组 每次推送都会产生重复运行,浪费分钟数 添加 concurrency: { group: ..., cancel-in-progress: true }
分片时使用 fail-fast: true 一个分片失败会取消其他分片,丢失它们的测试结果 设置 fail-fast: false 以收集所有失败
安装浏览器时未使用缓存 每次运行浪费 60-90 秒 以 lockfile 哈希为键,缓存 ~/.cache/ms-playwright
未设置 timeout-minutes 卡住的 job 将运行 6 小时(GitHub 默认值) 设置明确的超时时间:20-30 分钟
仅在失败时上传构建产物 测试通过时没有报告,无法验证结果 使用 if: ${{ !cancelled() }} 始终上传
在工作流文件中硬编码密钥 安全漏洞 使用 GitHub Secrets 和 Environments
在每个 PR 上运行所有浏览器 成本增加 3 倍,收益甚微 PR 上仅 Chromium;合并到主分支时做跨浏览器测试
actions/upload-artifact 未设置保留期限 默认 90 天保留期会填满存储 为报告设置 retention-days: 7-14
安装浏览器时未使用 --with-deps 缺少操作系统库导致浏览器启动失败 始终使用 npx playwright install --with-deps

故障排查

浏览器启动失败:"Missing dependencies"

原因:浏览器从缓存安装,但操作系统依赖未被缓存(它们位于系统目录中,而非 ~/.cache)。

修复:在缓存命中时始终运行 npx playwright install-deps

- name: Install Playwright OS dependencies
  if: steps.playwright-cache.outputs.cache-hit == 'true'
  run: npx playwright install-deps

测试本地通过但 CI 中超时失败

原因:CI 运行器的 CPU 核心数和 RAM 少于你的开发机器。默认的 worker 数和超时时间设置得太激进。

修复:在配置中为 CI 减少 worker 数并增加超时时间:

// playwright.config.ts
import { defineConfig } from "@playwright/test"

export default defineConfig({
  workers: process.env.CI ? "50%" : undefined,
  use: {
    actionTimeout: process.env.CI ? 15_000 : 10_000,
    navigationTimeout: process.env.CI ? 30_000 : 15_000,
  },
})

分片报告不完整——合并报告中缺少部分分片

原因:使用 actions/download-artifact@v4 时未设置 merge-multiple: true,或者分片之间的构建产物名称冲突。

修复:为每个分片赋予唯一的构建产物名称,并使用 merge-multiple

# 在每个分片 job 中上传
- uses: actions/upload-artifact@v4
  with:
    name: blob-report-${{ strategy.job-index }}
    path: blob-report/

# 在合并 job 中下载
- uses: actions/download-artifact@v4
  with:
    path: all-blob-reports
    pattern: blob-report-*
    merge-multiple: true

webServer 在 CI 中失败:"port 3000 already in use"

原因:前一次运行留下了僵尸进程,或者其他 job 步骤正在使用该端口。

修复:确保在 CI 中设置 reuseExistingServer: false,并添加一个前置步骤来杀死过期进程:

- name: Kill stale processes
  run: lsof -ti:3000 | xargs kill -9 2>/dev/null || true

GitHub 注释未在 PR 上显示

原因:未配置 github reporter。

修复:为 CI 运行添加 github reporter

// playwright.config.ts
import { defineConfig } from "@playwright/test"

export default defineConfig({
  reporter: process.env.CI
    ? [["html", { open: "never" }], ["github"]]
    : [["html", { open: "on-failure" }]],
})

相关文档