Day 10: CI/CDとベストプラクティス
今日学ぶこと
- CI/CDでPlaywrightを実行する理由
- GitHub Actionsでのセットアップ
- テストアーティファクト(レポート、スクリーンショット、動画、トレース)
- DockerでのPlaywright実行
- 認証状態の再利用(storageState)
- テスト設計のベストプラクティス
- フレイキーテストへの対処
- テスト戦略:E2E vs ユニット/インテグレーション
- 10日間の振り返り
なぜCI/CDでPlaywrightを実行するのか
ローカルでの手動テスト実行だけでは、品質を継続的に保つことは困難です。
flowchart LR
subgraph Manual["手動テスト"]
M1["忘れがち"]
M2["環境差異"]
M3["時間がかかる"]
end
subgraph CICD["CI/CD自動テスト"]
C1["毎回自動実行"]
C2["統一環境"]
C3["高速フィードバック"]
end
style Manual fill:#ef4444,color:#fff
style CICD fill:#22c55e,color:#fff
CI/CDパイプラインにPlaywrightを組み込むことで、コードの変更が既存機能を壊していないことを自動的に確認できます。
GitHub Actionsでのセットアップ
基本的なワークフロー
# .github/workflows/playwright.yml
name: Playwright Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
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
- name: Upload HTML report
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
ワークフローの流れ
flowchart TB
subgraph CI["GitHub Actions ワークフロー"]
A["コードをチェックアウト"] --> B["Node.jsセットアップ"]
B --> C["依存関係インストール"]
C --> D["ブラウザインストール"]
D --> E["テスト実行(ヘッドレス)"]
E --> F{"成功?"}
F -->|"Yes"| G["完了"]
F -->|"No"| H["レポート・トレース保存"]
H --> I["失敗を報告"]
end
style CI fill:#8b5cf6,color:#fff
style G fill:#22c55e,color:#fff
style I fill:#ef4444,color:#fff
ブラウザキャッシュの活用
ブラウザのインストールは時間がかかるため、キャッシュを使うと高速化できます。
- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
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 system dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps
テストアーティファクト
CI環境でテストが失敗したとき、原因を調査するためにアーティファクトが不可欠です。
playwright.config.ts での設定
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [
['html', { open: 'never' }],
['junit', { outputFile: 'results.xml' }],
],
use: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'retain-on-failure',
},
});
アーティファクトの種類
| アーティファクト | 設定値 | 用途 |
|---|---|---|
| HTMLレポート | reporter: 'html' |
テスト結果の可視化 |
| スクリーンショット | screenshot: 'only-on-failure' |
失敗時の画面キャプチャ |
| 動画 | video: 'retain-on-failure' |
失敗時のテスト再生 |
| トレース | trace: 'retain-on-failure' |
詳細なデバッグ情報 |
| JUnitレポート | reporter: 'junit' |
CI/CDツールとの連携 |
GitHub Actionsでのアップロード
- name: Upload test results
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: |
playwright-report/
test-results/
retention-days: 30
test-results/ ディレクトリにはスクリーンショット、動画、トレースファイルが保存されます。
DockerでのPlaywright実行
Playwrightは公式のDockerイメージを提供しています。
公式イメージの使用
FROM mcr.microsoft.com/playwright:v1.50.0-noble
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npx", "playwright", "test"]
GitHub Actionsでの使用
jobs:
test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.50.0-noble
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright test
env:
HOME: /root
コンテナイメージを使うと、ブラウザのインストール手順が不要になり、環境の一貫性も保証されます。
認証状態の再利用(storageState)
多くのテストでログインが必要な場合、毎回UIでログインするのは非効率です。Playwrightの storageState を使って認証状態を保存・再利用できます。
セットアッププロジェクトの定義
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'setup',
testMatch: /.*\.setup\.ts/,
},
{
name: 'chromium',
use: {
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},
],
});
認証セットアップファイル
// tests/auth.setup.ts
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('test@example.com');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL('/dashboard');
await expect(page.getByText('Welcome')).toBeVisible();
// Save authentication state
await page.context().storageState({ path: authFile });
});
flowchart TB
subgraph Auth["認証状態の再利用"]
A["setup プロジェクト"] --> B["ログイン実行"]
B --> C["storageState を保存"]
C --> D["chromium プロジェクト"]
D --> E["保存された状態を読み込み"]
E --> F["ログイン済みの状態でテスト"]
end
style Auth fill:#3b82f6,color:#fff
この方法により、テストごとにログインする必要がなくなり、テスト全体の実行時間を大幅に短縮できます。
テスト設計のベストプラクティス
プロジェクト構成
project-root/
├── tests/
│ ├── auth.setup.ts # 認証セットアップ
│ ├── auth/
│ │ ├── login.spec.ts
│ │ └── register.spec.ts
│ ├── dashboard/
│ │ ├── overview.spec.ts
│ │ └── settings.spec.ts
│ └── products/
│ ├── list.spec.ts
│ └── detail.spec.ts
├── pages/ # Page Object Model
│ ├── LoginPage.ts
│ ├── DashboardPage.ts
│ └── ProductPage.ts
├── fixtures/ # テストデータ
│ └── test-data.json
├── playwright.config.ts
└── package.json
メンテナブルなテストを書くコツ
// NG: 壊れやすいセレクタ
await page.locator('.btn-primary.mt-4.px-6').click();
await page.locator('#root > div > div:nth-child(3) > button').click();
// OK: ロールベースのロケータ
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByLabel('Email').fill('test@example.com');
await page.getByTestId('checkout-button').click();
ロケータの優先順位
| 優先度 | ロケータ | 理由 |
|---|---|---|
| 1 | getByRole() |
アクセシビリティに基づく、最も安定 |
| 2 | getByLabel() |
フォーム要素に最適 |
| 3 | getByText() |
表示テキストで特定 |
| 4 | getByTestId() |
テスト専用属性 |
| 5 | locator('.class') |
最後の手段 |
テストの独立性
// NG: テスト間に依存関係がある
test('create item', async ({ page }) => {
// ...create item...
});
test('edit item', async ({ page }) => {
// create item のテストが先に実行される前提
});
// OK: 各テストが独立している
test('edit item', async ({ page }) => {
// API経由でデータを準備
const item = await createTestItem(page);
await page.goto(`/items/${item.id}/edit`);
// ...
});
フレイキーテストへの対処
フレイキーテスト(Flaky Test)は、同じコードでも成功したり失敗したりする不安定なテストです。
flowchart TB
subgraph Causes["フレイキーの原因"]
C1["タイミング依存"]
C2["テスト間の状態共有"]
C3["外部サービス依存"]
C4["アニメーション"]
C5["動的コンテンツ"]
end
subgraph Solutions["対策"]
S1["auto-waiting を信頼"]
S2["テストの独立性確保"]
S3["APIモック"]
S4["アニメーション無効化"]
S5["明示的な待機"]
end
C1 --> S1
C2 --> S2
C3 --> S3
C4 --> S4
C5 --> S5
style Causes fill:#ef4444,color:#fff
style Solutions fill:#22c55e,color:#fff
リトライの設定
// playwright.config.ts
export default defineConfig({
retries: process.env.CI ? 2 : 0, // CIでは2回リトライ
});
フレイキーテストの検出
# 同じテストを10回繰り返して安定性を確認
npx playwright test --repeat-each=10 tests/checkout.spec.ts
対処のチェックリスト
| チェック項目 | 確認内容 |
|---|---|
固定の waitForTimeout() |
auto-waitingで代替できないか |
| テスト順序依存 | 単独で実行しても成功するか |
| 外部API依存 | route.fulfill() でモックしているか |
| 日付/時刻依存 | page.clock を使っているか |
| ランダムデータ | 固定のテストデータを使っているか |
テスト戦略:何をE2Eでテストするか
すべてをE2Eテストでカバーする必要はありません。テストピラミッドを意識しましょう。
flowchart TB
subgraph Pyramid["テストピラミッド"]
E2E["E2Eテスト<br/>少数・重要フロー"]
INT["インテグレーションテスト<br/>コンポーネント連携"]
UNIT["ユニットテスト<br/>多数・高速"]
end
E2E --> INT --> UNIT
style E2E fill:#ef4444,color:#fff
style INT fill:#f59e0b,color:#000
style UNIT fill:#22c55e,color:#fff
テスト種別の使い分け
| テスト種別 | 対象 | 例 |
|---|---|---|
| ユニットテスト | 個別の関数・クラス | バリデーションロジック、計算処理 |
| インテグレーション | コンポーネントの連携 | APIとDBの連携、UIコンポーネント |
| E2Eテスト | ユーザーフロー全体 | ログイン→商品購入→決済 |
E2Eテストで重点を置くべきシナリオ
- クリティカルパス: ログイン、決済、登録など
- クロスブラウザの検証: レイアウト崩れ、互換性
- ユーザージャーニー: 一連の操作フロー
- リグレッション防止: 過去に発生したバグの再発防止
10日間のまとめ
flowchart TB
subgraph Journey["10日間の学習の道のり"]
D1["Day 1<br/>Playwrightとは"]
D2["Day 2<br/>セットアップ"]
D3["Day 3<br/>ロケータ"]
D4["Day 4<br/>アサーション"]
D5["Day 5<br/>ページ操作"]
D6["Day 6<br/>ネットワーク"]
D7["Day 7<br/>Page Object"]
D8["Day 8<br/>フィクスチャ"]
D9["Day 9<br/>デバッグ"]
D10["Day 10<br/>CI/CD"]
D1 --> D2 --> D3 --> D4 --> D5
D5 --> D6 --> D7 --> D8 --> D9 --> D10
end
style D1 fill:#3b82f6,color:#fff
style D2 fill:#3b82f6,color:#fff
style D3 fill:#8b5cf6,color:#fff
style D4 fill:#8b5cf6,color:#fff
style D5 fill:#8b5cf6,color:#fff
style D6 fill:#f59e0b,color:#000
style D7 fill:#f59e0b,color:#000
style D8 fill:#f59e0b,color:#000
style D9 fill:#22c55e,color:#fff
style D10 fill:#22c55e,color:#fff
| Day | テーマ | 学んだこと |
|---|---|---|
| 1 | Playwrightとは | E2Eテストの概要、Playwrightの特徴とアーキテクチャ |
| 2 | セットアップ | インストール、設定、最初のテスト作成 |
| 3 | ロケータとDOM操作 | getByRole, getByText, locator の使い分け |
| 4 | アサーション | expect, toBeVisible, toHaveText の活用 |
| 5 | ページ操作とナビゲーション | フォーム操作、ファイルアップロード、ダイアログ |
| 6 | ネットワーク制御 | route.fulfill, waitForResponse, APIモック |
| 7 | Page Object Model | 再利用可能なページクラスの設計 |
| 8 | フィクスチャとデータ管理 | カスタムフィクスチャ、テストデータの管理 |
| 9 | デバッグとトレース | Trace Viewer, UI Mode, デバッグ手法 |
| 10 | CI/CDとベストプラクティス | GitHub Actions、Docker、テスト戦略 |
まとめ
| 概念 | 説明 |
|---|---|
| GitHub Actions | CI/CDでのテスト自動実行 |
| ブラウザキャッシュ | CI実行時間の短縮 |
| アーティファクト | レポート、スクリーンショット、動画、トレース |
| Docker | 公式イメージによる環境統一 |
| storageState | 認証状態の保存と再利用 |
| リトライ | CIでのフレイキーテスト対策 |
| テストピラミッド | E2E/インテグレーション/ユニットの使い分け |
重要ポイント
- CI/CDでテストを自動化して品質を守る
- storageStateで認証処理を効率化する
- フレイキーテストは放置せず根本原因を解決する
- テストピラミッドを意識してE2Eテストの範囲を適切に保つ
- ロールベースのロケータで壊れにくいテストを書く
練習問題
基本
- GitHub Actionsのワークフローファイルを作成し、プッシュ時にPlaywrightテストが自動実行されるようにしてください。
playwright.config.tsでスクリーンショット、動画、トレースの設定を行ってください。- HTMLレポートをアーティファクトとしてアップロードする設定を追加してください。
応用
storageStateを使った認証セットアッププロジェクトを作成し、テスト実行時間を短縮してください。- Dockerの公式イメージを使ってPlaywrightテストを実行してください。
--repeat-each=10を使って、既存テストのフレイキーさを確認してください。
チャレンジ
- 複数ブラウザ(Chromium, Firefox, WebKit)でテストを並列実行するGitHub Actionsワークフローを設計してください。失敗時にトレースファイルをアップロードする設定も含めてください。
参考リンク
- Playwright CI/CD Guide
- Playwright GitHub Actions
- Playwright Docker
- Playwright Authentication
- Playwright Best Practices
おめでとうございます!
10日間のPlaywright学習が完了しました!
学んだこと
- Day 1: Playwrightとは何か、E2Eテストの重要性
- Day 2: 環境構築と最初のテスト
- Day 3: ロケータによるDOM操作
- Day 4: アサーションでテストを検証
- Day 5: ページ操作とナビゲーション
- Day 6: ネットワークリクエストの制御
- Day 7: Page Object Modelで再利用性向上
- Day 8: フィクスチャとテストデータ管理
- Day 9: デバッグ手法とトレース
- Day 10: CI/CDとベストプラクティス
これからの道
あなたはもうPlaywrightの基礎をしっかりと身につけました。ここからは実際のプロジェクトでテストを書き、経験を積んでいくことが最も大切です。
Playwrightは活発に開発が続けられているツールです。新しいバージョンがリリースされるたびに便利な機能が追加されています。公式ドキュメントを定期的にチェックし、最新の機能やベストプラクティスを取り入れていきましょう。
テストを書くことは、ソフトウェアの品質を守るだけでなく、自信を持ってコードを変更し、素早くリリースすることを可能にします。今日学んだCI/CDの知識を活かして、チームの開発フローにPlaywrightを組み込んでいきましょう。
テストの旅はここから始まります。学び続けて、信頼性の高いWebアプリケーションを作りましょう!