はじめに
「ローカルでは全部テストが通るのに、GitHub Actions(CI)にプッシュしたら突然落ちる」——Playwrightを使い始めた開発者が最初にぶつかる壁がこれです。
この記事では、CIでPlaywrightテストが落ちる5つの原因と、それぞれの対処法を解説します。
症状チェック:こんな症状はありませんか?
- ローカル(
npx playwright test)では全テストがパスする - GitHub Actionsのログに
TimeoutErrorやError: page.gotoが出る - スクリーンショット比較テストが
snapshot doesn't matchで落ちる Page closedやTarget closedエラーが突然出る- 同じコミットでもCIを再実行すると通ったり落ちたりする(フレーキー)
原因1:ブラウザのインストール忘れ
症状
Error: browserType.launch: Executable doesn't exist at /home/runner/.cache/ms-playwright/chromium-xxx/chrome-linux/chrome
原因
GitHub Actionsのランナーにはブラウザがプリインストールされていません。npx playwright install を実行しないと、ブラウザが存在しないためテストが起動すらしません。
対策
# .github/workflows/playwright.yml
- name: Install Playwright Browsers
run: npx playwright install --with-deps
--with-deps フラグが重要です。これがないと、ブラウザの実行に必要なシステム依存パッケージ(libgbm、libasound2 など)がインストールされず、ブラウザが起動しません。
原因2:タイムアウトが足りない(CI環境はローカルより遅い)
症状
TimeoutError: page.click: Timeout 5000ms exceeded.
原因
CIのランナーはローカルマシンよりCPUが遅く、ネットワーク遅延も大きいです。ローカルでは100ms以内に応答する要素が、CI上では2〜3秒かかることがあります。
デフォルトのタイムアウト(5000ms)だとCI上でギリギリ足りないケースが多発します。
対策
playwright.config.ts でタイムアウトを伸ばします:
// playwright.config.ts
import { defineConfig } from '@playwright/test'
export default defineConfig({
timeout: 30000, // テスト全体のタイムアウト(デフォルト: 30000ms)
expect: {
timeout: 10000, // expect() のタイムアウト(デフォルト: 5000ms)
},
use: {
actionTimeout: 15000, // click等のアクションのタイムアウト
navigationTimeout: 30000, // page.goto() のタイムアウト
},
})
CIのみ設定を変えたい場合:
export default defineConfig({
timeout: ({}).CI ? 60000 : 30000,
use: {
actionTimeout: ({}).CI ? 30000 : 10000,
},
})
原因3:スナップショット(ビジュアルリグレッション)のベースラインが違う
症状
Error: A snapshot doesn't exist at tests/screenshot.spec.ts-snapshots/homepage-linux.png, writing actual.
または
Screenshot comparison failed: 1234 pixels are different.
原因
Playwrightのスナップショットテスト(expect(page).toHaveScreenshot())は、OSとブラウザのバージョンごとにベースライン画像が異なります。
ローカル(macOS)で生成したベースライン画像をコミットしても、CI(Linux)では使えません。フォントのレンダリング、アンチエイリアス、スケーリングが違うため、ピクセル差が必ず発生します。
対策
方法1: Linux向けのベースラインをDockerで生成する
# Docker経由でLinux用のスナップショットを生成
docker run --rm \
-v $(pwd):/work \
-w /work \
mcr.microsoft.com/playwright:v1.44.0-jammy \
npx playwright test --update-snapshots
生成された -linux.png をコミットすればCIで一致します。
方法2: スナップショットを許容範囲で比較する
await expect(page).toHaveScreenshot({
maxDiffPixels: 100, // 100ピクセルまで差異を許容
threshold: 0.1, // 10%の色差まで許容
})
原因4:環境変数・認証情報がCIにない
症状
- テストが認証エラーで落ちる
({}).API_URLがundefinedになる- APIモックが機能せず実際のAPIにアクセスしてレート制限に引っかかる
原因
ローカルの .env ファイルに書いた環境変数は、CIに自動コピーされません。
対策
GitHub Actions の secrets を使う:
# .github/workflows/playwright.yml
- name: Run Playwright tests
run: npx playwright test
env:
API_URL: ${{ secrets.API_URL }}
AUTH_TOKEN: ${{ secrets.AUTH_TOKEN }}
BASE_URL: http://localhost:3000
playwright.config.ts で baseURL を明示する:
export default defineConfig({
use: {
baseURL: ({}).BASE_URL ?? 'http://localhost:3000',
},
})
APIをモックしてCIの外部依存を排除する:
// tests/example.spec.ts
test('ユーザー一覧が表示される', async ({ page }) => {
// 外部APIをモックする
await page.route('/api/users', (route) => {
route.fulfill({
status: 200,
body: JSON.stringify([{ id: 1, name: 'テストユーザー' }]),
})
})
await page.goto('/')
await expect(page.getByText('テストユーザー')).toBeVisible()
})
原因5:開発サーバーが起動していない・ポートが違う
症状
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000
原因
Playwrightはテスト実行前に開発サーバーが起動していることを前提とします。ローカルでは npm run dev を先に起動してからテストしますが、CIでは何もしていないとサーバーがいません。
対策
playwright.config.ts の webServer オプションで自動起動します:
// playwright.config.ts
export default defineConfig({
webServer: {
command: 'npm run build && npm run preview',
url: 'http://localhost:4173',
reuseExistingServer: !({}).CI,
timeout: 120 * 1000, // サーバー起動待ちのタイムアウト(2分)
},
use: {
baseURL: 'http://localhost:4173',
},
})
reuseExistingServer: !({}).CI とすることで、ローカルでは既存サーバーを再利用(速い)し、CIでは毎回新しく起動します。
開発サーバー(npm run dev)ではなくビルド済みプレビュー(npm run build && npm run preview)を使うと、本番に近い環境でテストできます。
まとめ:CIでPlaywrightが落ちる原因チェックリスト
| 原因 | エラーの特徴 | 対策 |
|---|---|---|
| ブラウザ未インストール | Executable doesn't exist |
npx playwright install --with-deps をworkflowに追加 |
| タイムアウト不足 | TimeoutError が出る |
playwright.config.ts でCI用にタイムアウトを延長 |
| スナップショット不一致 | snapshot doesn't match |
Linux環境でベースラインを再生成、または許容差を設定 |
| 環境変数なし | 認証エラー・undefined |
GitHub Secrets経由で注入、またはAPIをモック |
| サーバー未起動 | ERR_CONNECTION_REFUSED |
webServer オプションでCI内でサーバーを自動起動 |
CIでPlaywrightが落ちる原因の9割以上はこの5つです。GitHub Actionsのログをよく読んで、エラーメッセージから原因を絞り込んでください。
GitHub Actions の完全なワークフロー例
name: Playwright Tests
on:
push:
branches: [main, production]
pull_request:
branches: [main]
jobs:
test:
timeout-minutes: 60
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: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
env:
CI: true
BASE_URL: http://localhost:4173
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
失敗時にレポートをアーティファクトとしてアップロードするとデバッグが楽になります。HTMLレポートにはスクリーンショット・動画・トレースが含まれており、原因特定に役立ちます。
