首页
/ Playwright CI 实战指南:在持续集成环境中稳定运行浏览器自动化测试

Playwright CI 实战指南:在持续集成环境中稳定运行浏览器自动化测试

2026-09-06 21:34:07作者:裴麒琰

本文基于 Playwright 官方文档 docs/src/ci.md 编写,系统讲解如何把 Playwright 测试接入各类 CI 平台(GitHub Actions、Azure Pipelines、CircleCI、Jenkins、GitLab CI 等),覆盖浏览器依赖安装、workers 与 globalTimeout 两大关键配置、各平台完整工作流配置、分片(sharding)并行策略,以及浏览器启动故障排查与有头模式运行等实战技巧。读完本文,你可以直接复制可用的 CI 配置,并结合 Playwright 源码理解每个配置项背后的实现机制。

三步走:让 CI Agent 跑起 Playwright 测试

Playwright 测试完全可以在 CI 环境中执行,官方为常见 CI 提供商提供了示例配置。整体流程分为三步:

第 1 步:确保 CI Agent 能运行浏览器。在 Linux agent 上使用官方 Docker 镜像(见 docs/src/docker.md),或使用 Playwright CLI 安装系统依赖(--with-deps,该选项在 CLI 中的定义可参考 packages/playwright-core/src/cli/program.ts)。

第 2 步:安装 Playwright。各语言生态的安装命令如下:

# Install NPM packages
npm ci

# Install Playwright browsers and dependencies
npx playwright install --with-deps
pip install playwright
playwright install --with-deps
mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps"
dotnet build
pwsh bin/Debug/netX/playwright.ps1 install --with-deps

第 3 步:运行测试

npx playwright test
pytest
mvn test
dotnet test

关键配置一:CI 环境中的 workers 设置

官方建议在 CI 环境中把 workers 设置为 1,优先保证稳定性与可复现性。顺序执行确保每个测试独占全部系统资源,避免潜在冲突。如果你拥有性能强劲的自托管 CI 系统,也可以启用并行;对于更宽的并行度,考虑分片(sharding)——把测试分发到多台 CI 机器上。

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

export default defineConfig({
  // Opt out of parallel tests on CI.
  workers: process.env.CI ? 1 : undefined,
});

源码佐证:从 packages/playwright/src/common/config.ts 可以看到,当配置中没有显式指定 workers 时,默认值是 '50%';而 resolveWorkers 函数 会把百分比解析为「CPU 核数 × 百分比」并至少为 1。这意味着在 CI 容器(如 GitHub Actions 的 2 核 runner)上不设置 workers 时,Playwright 默认就会按核数并行——这正是官方建议显式设为 1 的原因:核心数很少的 CI 机器上盲目并行容易造成资源争抢与偶发超时。

关键配置二:始终设置 globalTimeout

在 CI 中务必设置 global timeout。默认情况下,一次测试运行没有上限时长:一旦某个测试套件挂起、或随着测试规模增长超出了 CI 提供商的 job 时限,运行会在中途被 runner 杀掉,从而无法生成测试报告

设置了 globalTimeout 后,Playwright 会自己停止整个运行:

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

export default defineConfig({
  // Fail the run after an hour, so that the reporters still produce a report.
  globalTimeout: 60 * 60 * 1000,
});

源码佐证:配置解析位于 packages/playwright/src/common/config.tsglobalTimeout 的解析优先级为「CLI 覆盖值 → 用户配置值 → 默认值 0」,其中 0 表示不限时——这与文档中「默认没有上限」的说明完全一致。执行侧的 runTasks 函数 只有在 globalTimeout 大于 0 时才会计算 deadline;超时后 TaskRunner 会中断后续任务并向 reporter 上报 Timed out waiting Xs for the ... to run 错误,保证 reporter 有机会收尾输出完整报告。

此外,下文给出的示例均不设置 job 级超时(如 GitHub Actions 的 timeout-minutes)。如果你确需添加,请让它显著高于 globalTimeout,确保总是由 Playwright 先停止。

CI 平台配置:GitHub Actions

Playwright 的命令行工具可以在 CI 中安装所有操作系统依赖。

push/pull_request 触发(JavaScript)

测试将在 main/master 分支的 push 或 pull request 时运行。工作流会安装全部依赖、安装 Playwright 浏览器,然后运行测试,并上传 HTML 报告作为 artifact:

name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
    - uses: actions/setup-node@v6
      with:
        node-version: lts/*
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright Browsers
      run: npx playwright install --with-deps
    - name: Run Playwright tests
      run: npx playwright test
    - uses: actions/upload-artifact@v5
      if: ${{ !cancelled() }}
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30

push/pull_request 触发(Python / Java / .NET)

name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
    - name: Set up Python
      uses: actions/setup-python@v6
      with:
        python-version: '3.13'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Ensure browsers are installed
      run: python -m playwright install --with-deps
    - name: Run your tests
      run: pytest --tracing=retain-on-failure
    - uses: actions/upload-artifact@v5
      if: ${{ !cancelled() }}
      with:
        name: playwright-traces
        path: test-results/
name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
    - uses: actions/setup-java@v5
      with:
        distribution: 'temurin'
        java-version: '25'
    - name: Build & Install
      run: mvn -B install -D skipTests --no-transfer-progress
    - name: Ensure browsers are installed
      run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps"
    - name: Run tests
      run: mvn test
name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
    - name: Setup dotnet
      uses: actions/setup-dotnet@v5
      with:
        dotnet-version: 8.0.x
    - name: Build & Install
      run: dotnet build
    - name: Ensure browsers are installed
      run: pwsh bin/Debug/net8.0/playwright.ps1 install --with-deps
    - name: Run your tests
      run: dotnet test

分片执行(Sharded)

GitHub Actions 支持跨多个 job 分片测试,可参考官方 sharding 文档了解分片原理、GitHub Actions 示例(如何把测试分发到多台机器运行)以及如何合并 HTML 报告。

容器化运行(Via Containers)

通过 jobs.<job_id>.container 选项让 job 运行在容器内,好处是:不污染宿主环境依赖;为截图/视觉回归测试在不同操作系统间提供一致的环境。

JavaScript 版:

name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  playwright:
    name: 'Playwright Tests'
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright:v%%VERSION%%-noble
      options: --user 1001
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: lts/*
      - name: Install dependencies
        run: npm ci
      - name: Run your tests
        run: npx playwright test

Python / Java / .NET 版本分别使用 mcr.microsoft.com/playwright/python:v%%VERSION%%-noblemcr.microsoft.com/playwright/java:v%%VERSION%%-noblemcr.microsoft.com/playwright/dotnet:v%%VERSION%%-noble 镜像,容器内已完成浏览器与系统依赖预装,因此步骤中不需要再执行 install --with-deps,只需安装依赖包并直接运行测试即可(%%VERSION%% 为文档构建时替换为具体 Playwright 版本的占位符)。

部署后触发(On deployment)

以下工作流在 GitHub Deployment 进入 success 状态后启动测试。Vercel 等 PaaS 服务支持这一模式,你可以在它们部署出的环境上运行端到端测试:

name: Playwright Tests
on:
  deployment_status:
jobs:
  test:
    runs-on: ubuntu-latest
    if: github.event.deployment_status.state == 'success'
    steps:
    - uses: actions/checkout@v6
    - uses: actions/setup-node@v6
      with:
        node-version: lts/*
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright
      run: npx playwright install --with-deps
    - name: Run Playwright tests
      run: npx playwright test
      env:
        PLAYWRIGHT_TEST_BASE_URL: ${{ github.event.deployment_status.target_url }}

Python、Java、.NET 版本的写法同理:监听 deployment_status 事件、在 success 时运行,并把 PLAYWRIGHT_TEST_BASE_URL 环境变量指向 ${{ github.event.deployment_status.target_url }}(不同测试 runner 对环境变量名的约定可能不同,需自行适配)。

Fail-Fast:--only-changed 预检

大型测试套件执行耗时很长。通过 --only-changed 参数先做一轮预检,可以优先执行最可能失败的测试文件,在 Pull Request 阶段获得更快的反馈、并略微降低 CI 消耗。--only-changed 通过分析测试套件的依赖图来检测受改动影响的测试文件——这是一种启发式方法,可能漏掉测试,因此预检之后必须始终跑完整测试套件。

name: Playwright Tests
on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
      with:
        # Force a non-shallow checkout, so that we can reference $GITHUB_BASE_REF.
        fetch-depth: 0
    - uses: actions/setup-node@v6
      with:
        node-version: lts/*
    - name: Install dependencies
      run: npm ci
    - name: Install Playwright Browsers
      run: npx playwright install --with-deps
    - name: Run changed Playwright tests
      run: npx playwright test --only-changed=origin/$GITHUB_BASE_REF
      if: github.event_name == 'pull_request'
    - name: Run Playwright tests
      run: npx playwright test
    - uses: actions/upload-artifact@v5
      if: ${{ !cancelled() }}
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30

源码佐证:CLI 参数定义于 packages/playwright/src/program.ts,其帮助文本说明该参数「只运行 HEADref 之间有变更的测试文件,默认对比所有未提交的改动,仅支持 Git」。实际检测逻辑在 packages/playwright/src/runner/vcs.tsdetectChangedTestFiles 中:它执行 git diff <base> --name-onlygit ls-files --others --exclude-standard 收集变更文件,再交给 cc.affectedTestFiles 换算为受影响的测试文件。值得注意的是该函数会显式检测 shallow clonegit rev-parse --is-shallow-repository)——这正是上方 YAML 中必须设置 fetch-depth: 0 的原因,否则浅克隆仓库无法解析基线引用,会直接抛出错误。

CI 平台配置:Docker

官方提供了预构建 Docker 镜像,可直接使用,也可作为参考来改造你自己的 Docker 定义。建议遵循 Recommended Docker Configuration 以获得最佳性能。

docs/src/docker.md 可以看到三条关键推荐:

  1. 使用 --init 标志,避免 PID=1 进程的特殊处理(僵尸进程的常见原因);
  2. 使用 Chromium 时建议使用 --ipc=host,否则 Chromium 可能内存不足而崩溃;
  3. 若本地开发时 Chromium 启动出现奇怪错误,可尝试 docker run --cap-add=SYS_ADMIN

CI 平台配置:Azure Pipelines

Windows 或 macOS agent 无需额外配置,安装 Playwright 后直接运行测试即可。Linux agent 可选用官方 Docker 容器运行容器化 job,或使用命令行工具安装所有必要依赖。

JavaScript:

trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: UseNode@1
  inputs:
    version: '22'
  displayName: 'Install Node.js'
- script: npm ci
  displayName: 'npm ci'
- script: npx playwright install --with-deps
  displayName: 'Install Playwright browsers'
- script: npx playwright test
  displayName: 'Run Playwright tests'
  env:
    CI: 'true'

Python:

trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: UsePythonVersion@0
  inputs:
    versionSpec: '3.13'
  displayName: 'Use Python'
- script: |
    python -m pip install --upgrade pip
    pip install -r requirements.txt
  displayName: 'Install dependencies'
- script: playwright install --with-deps
  displayName: 'Install Playwright browsers'
- script: pytest
  displayName: 'Run Playwright tests'

Java:

trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: JavaToolInstaller@1
  inputs:
    versionSpec: '25'
    jdkArchitectureOption: 'x64'
    jdkSourceOption: AzureStorage
- script: mvn -B install -D skipTests --no-transfer-progress
  displayName: 'Build and install'
- script: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps"
  displayName: 'Install Playwright browsers'
- script: mvn test
  displayName: 'Run tests'

.NET:

trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: UseDotNet@2
  inputs:
    packageType: sdk
    version: '8.0.x'
  displayName: 'Use .NET SDK'
- script: dotnet build --configuration Release
  displayName: 'Build'
- script: pwsh bin/Release/net8.0/playwright.ps1 install --with-deps
  displayName: 'Install Playwright browsers'
- script: dotnet test --configuration Release
  displayName: 'Run tests'

上传 playwright-report 并集成测试结果

以下配置让 pipeline 在任一 Playwright 测试失败时整体失败;同时通过 PublishTestResults 任务把测试结果集成进 Azure DevOps:

trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: UseNode@1
  inputs:
    version: '22'
  displayName: 'Install Node.js'

- script: npm ci
  displayName: 'npm ci'
- script: npx playwright install --with-deps
  displayName: 'Install Playwright browsers'
- script: npx playwright test
  displayName: 'Run Playwright tests'
  env:
    CI: 'true'
- task: PublishTestResults@2
  displayName: 'Publish test results'
  inputs:
    searchFolder: 'test-results'
    testResultsFormat: 'JUnit'
    testResultsFiles: 'e2e-junit-results.xml'
    mergeTestResults: true
    failTaskOnFailedTests: true
    testRunTitle: 'My End-To-End Tests'
  condition: succeededOrFailed()
- task: PublishPipelineArtifact@1
  inputs:
    targetPath: playwright-report
    artifact: playwright-report
    publishLocation: 'pipeline'
  condition: succeededOrFailed()

注意:JUnit reporter 需要在 playwright.config.ts 中相应配置:

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

export default defineConfig({
  reporter: [['junit', { outputFile: 'test-results/e2e-junit-results.xml' }]],
});

分片执行(Azure Pipelines, sharded)

trigger:
- main

pool:
  vmImage: ubuntu-latest

strategy:
  matrix:
    chromium-1:
      project: chromium
      shard: 1/3
    chromium-2:
      project: chromium
      shard: 2/3
    chromium-3:
      project: chromium
      shard: 3/3
    firefox-1:
      project: firefox
      shard: 1/3
    firefox-2:
      project: firefox
      shard: 2/3
    firefox-3:
      project: firefox
      shard: 3/3
    webkit-1:
      project: webkit
      shard: 1/3
    webkit-2:
      project: webkit
      shard: 2/3
    webkit-3:
      project: webkit
      shard: 3/3
steps:
- task: UseNode@1
  inputs:
    version: '22'
  displayName: 'Install Node.js'

- script: npm ci
  displayName: 'npm ci'
- script: npx playwright install --with-deps
  displayName: 'Install Playwright browsers'
- script: npx playwright test --project=$(project) --shard=$(shard)
  displayName: 'Run Playwright tests'
  env:
    CI: 'true'

容器化执行(containerized)

在 job 层面直接指定 container(按语言选择对应镜像),容器内已预装浏览器,无需 install --with-deps

trigger:
- main

pool:
  vmImage: ubuntu-latest
container: mcr.microsoft.com/playwright:v%%VERSION%%-noble

steps:
- task: UseNode@1
  inputs:
    version: '22'
  displayName: 'Install Node.js'

- script: npm ci
  displayName: 'npm ci'
- script: npx playwright test
  displayName: 'Run Playwright tests'
  env:
    CI: 'true'

Python / Java / .NET 分别使用 mcr.microsoft.com/playwright/python:…mcr.microsoft.com/playwright/java:…mcr.microsoft.com/playwright/dotnet:… 镜像,步骤简化为「安装依赖 → 运行测试」即可。

CI 平台配置:CircleCI

在 CircleCI 上运行 Playwright 与 GitHub Actions 非常相似。在 config 的 agent 定义中加入 docker: 指定官方预构建镜像即可:

executors:
  pw-noble-development:
    docker:
      - image: mcr.microsoft.com/playwright:v%%VERSION%%-noble

Python / Java / .NET 同理,把镜像换成 mcr.microsoft.com/playwright/{python,java,dotnet}:v%%VERSION%%-noble

资源档位注意:使用 docker agent 定义时,你需要显式指定 Playwright 运行的 resource class 为 medium 档位。Playwright 的默认行为是把 workers 数设置为检测到的 CPU 核数(medium 档位为 2)。把 workers 数设置得高于核数会导致不必要的超时和失败——这与前文 resolveWorkers 按核数解析 '50%' 默认值的机制相呼应。

CircleCI 分片

CircleCI 的 CIRCLE_NODE_INDEX0 开始计数,因此需要覆盖默认并行环境变量:给 CIRCLE_NODE_INDEX 加 1 后再传给 --shard

    playwright-job-name:
      executor: pw-noble-development
      parallelism: 4
      steps:
        - run: SHARD="$((${CIRCLE_NODE_INDEX}+1))"; npx playwright test --shard=${SHARD}/${CIRCLE_NODE_TOTAL}

CI 平台配置:Jenkins

Jenkins 的 pipeline 支持 Docker agent,使用 Playwright Docker 镜像即可运行测试:

pipeline {
   agent { docker { image 'mcr.microsoft.com/playwright:v%%VERSION%%-noble' } }
   stages {
      stage('e2e-tests') {
         steps {
            sh 'npm ci'
            sh 'npx playwright test'
         }
      }
   }
}

Python 版本在容器内执行 pip install -r requirements.txt + pytest;Java 版本执行 mvn -B install -D skipTests --no-transfer-progress + mvn test;.NET 版本执行 dotnet build + dotnet test,镜像分别为 mcr.microsoft.com/playwright/{python,java,dotnet}:v%%VERSION%%-noble

CI 平台配置:Bitbucket Pipelines、GitLab CI、Google Cloud Build 与 Drone

Bitbucket Pipelines 支持将公共 Docker 镜像作为构建环境,直接使用官方镜像:

image: mcr.microsoft.com/playwright:v%%VERSION%%-noble

GitLab CI 同样使用官方公共 Docker 镜像:

stages:
  - test

tests:
  stage: test
  image: mcr.microsoft.com/playwright:v%%VERSION%%-noble
  script:
  ...

各语言镜像选择规则与其他平台一致(python / java / dotnet 变体)。

GitLab 分片:GitLab CI 支持用 parallel 关键字把 job 拆分为多个并行小 job(命名为 job_name 1/Njob_name N/N):

stages:
  - test

tests:
  stage: test
  image: mcr.microsoft.com/playwright:v%%VERSION%%-noble
  parallel: 7
  script:
    - npm ci
    - npx playwright test --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL

GitLab 还支持 parallel:matrix:单个 job 在一条 pipeline 内以不同变量值运行多次。下例中 2 个 PROJECT 值 × 10 个 SHARD 值 = 共 20 个 job:

stages:
  - test

tests:
  stage: test
  image: mcr.microsoft.com/playwright:v%%VERSION%%-noble
  parallel:
    matrix:
      - PROJECT: ['chromium', 'webkit']
        SHARD: ['1/10', '2/10', '3/10', '4/10', '5/10', '6/10', '7/10', '8/10', '9/10', '10/10']
  script:
    - npm ci
    - npx playwright test --project=$PROJECT --shard=$SHARD

Google Cloud Build

steps:
- name: mcr.microsoft.com/playwright:v%%VERSION%%-noble
  script:
  ...
  env:
  - 'CI=true'

Drone

kind: pipeline
name: default
type: docker

steps:
  - name: test
    image: mcr.microsoft.com/playwright:v%%VERSION%%-noble
    commands:
      - npx playwright test

不建议缓存浏览器二进制文件

官方不建议缓存浏览器二进制:恢复缓存所花的时间与直接下载二进制的时间相当,而且在 Linux 上还必须安装操作系统依赖——这部分是不可缓存的。

如果你仍然希望在 CI 运行之间缓存浏览器二进制,请缓存这些目录,并以 Playwright 版本的哈希作为缓存 key。

调试浏览器启动失败

Playwright 支持 DEBUG 环境变量在执行期间输出调试日志。排查 Error: Failed to launch browser 错误时,把它设为 pw:browser 会非常有帮助:

DEBUG=pw:browser npx playwright test
DEBUG=pw:browser pytest
DEBUG=pw:browser mvn test
DEBUG=pw:browser dotnet test

在 CI 中以有头模式(headed)运行测试

Playwright 默认以 headless 模式启动浏览器。Linux agent 上运行有头模式需要安装 Xvfb——官方 Docker 镜像 与 GitHub Action 已预装 Xvfb。在有 Xvfb 的环境中以有头模式运行浏览器,只需在命令前加 xvfb-run

xvfb-run npx playwright test
xvfb-run pytest
xvfb-run mvn test
xvfb-run dotnet test

小结:CI 配置要点速查

要点 建议 依据
浏览器依赖 npx playwright install --with-deps 或官方 Docker 镜像 docs/src/ci.mddocs/src/docker.md
workers CI 中设为 1,自托管强机器可并行、跨机用 shard docs/src/test-sharding.md、resolveWorkers
globalTimeout 始终设置(如 1 小时),且 job 级超时须高于它 runTasks
报告 容器化/镜像环境保证跨 OS 视觉一致;上传 playwright-report 各平台配置示例
大套件 --only-changed 预检 + 全量兜底;注意 fetch-depth: 0 detectChangedTestFiles
排查 DEBUG=pw:browser;有头模式用 xvfb-run docs/src/ci.md

以上所有配置均可在当前仓库的 docs/src/ci.md 中查阅原始版本,配合 docs/src/docker.md 的镜像说明与 docs/src/test-sharding.md 的分片详解,即可搭建完整的 Playwright CI 流水线。

登录后查看全文
热门项目推荐
相关项目推荐