T-CREATOR

Playwright × GitHub Actions 導入ガイド:キャッシュ最適化と並列戦略まで一気通貫

Playwright × GitHub Actions 導入ガイド:キャッシュ最適化と並列戦略まで一気通貫

モダンな Web 開発において、E2E テストの自動化は品質保証に欠かせません。特に Playwright と GitHub Actions を組み合わせることで、強力なテスト環境を構築できます。

本記事では、Playwright を GitHub Actions に導入する基礎から、実行時間を大幅に短縮するキャッシュ最適化、そして複数のテストを効率的に実行する並列戦略まで、一気通貫で解説していきますね。初めての方でも実践できるよう、各ステップを丁寧に説明します。

背景

E2E テストの重要性

Web アプリケーション開発において、単体テストだけでは実際のユーザー体験を保証できません。ブラウザ上での動作確認が必要です。

そこで登場するのが E2E(End-to-End)テストでしょう。実際のブラウザを操作してユーザーの行動をシミュレートすることで、本番環境に近い状態での動作検証が可能になります。

Playwright と GitHub Actions の組み合わせ

以下の図は、Playwright と GitHub Actions を組み合わせた際の基本的なワークフローを示しています。

mermaidflowchart LR
  developer["開発者"] -->|コード<br/>プッシュ| gh["GitHub<br/>リポジトリ"]
  gh -->|トリガー| actions["GitHub<br/>Actions"]
  actions -->|テスト<br/>実行| playwright["Playwright<br/>テスト"]
  playwright -->|結果<br/>報告| actions
  actions -->|通知| developer

Playwright は、Microsoft 製のブラウザ自動化ツールです。Chromium、Firefox、WebKit の 3 つのブラウザエンジンをサポートし、クロスブラウザテストが簡単に実現できます。

GitHub Actions は、GitHub が提供する CI/CD プラットフォームですね。コードがプッシュされたタイミングで自動的にテストを実行でき、開発フローに自然に組み込めます。

この 2 つを組み合わせることで、コード変更のたびに自動的に複数ブラウザでテストが実行され、品質を継続的に保てるようになります。

実運用における考慮事項

しかし、実際の運用では単に導入するだけでは不十分です。テストケースが増えるにつれて実行時間が長くなり、開発速度に影響を与えてしまいます。

そのため、以下の 3 つの観点が重要になってきます。

  • 導入: Playwright と GitHub Actions の基本的なセットアップ
  • キャッシュ最適化: 依存関係のインストール時間を短縮
  • 並列戦略: 複数のテストを同時実行して全体の時間を削減

これらを適切に組み合わせることで、高速で信頼性の高いテスト環境が構築できるでしょう。

課題

テスト実行時間の肥大化

E2E テストをそのまま導入すると、複数の課題に直面します。最も顕著なのが実行時間の問題です。

以下の図は、テスト実行時間の内訳を示しています。

mermaidflowchart TD
  start["テスト<br/>開始"] --> setup["環境<br/>セットアップ"]
  setup --> deps["依存関係<br/>インストール"]
  deps --> browser["ブラウザ<br/>インストール"]
  browser --> test["テスト<br/>実行"]
  test --> report["レポート<br/>生成"]
  report --> finish["完了"]

  style deps fill:#ffcccc
  style browser fill:#ffcccc
  style test fill:#ffffcc

特に問題となるのが、以下の 2 つの処理です。

依存関係のインストール

毎回 yarn install を実行すると、数百のパッケージをダウンロードする必要があります。プロジェクトの規模によっては、この処理だけで 3〜5 分かかることも珍しくありません。

ブラウザのインストール

Playwright は初回実行時にブラウザバイナリをダウンロードします。Chromium、Firefox、WebKit の 3 つをインストールすると、合計で数百 MB のダウンロードが発生してしまいます。

逐次実行による待ち時間

テストケースが増えると、すべてを順番に実行していては時間がかかりすぎます。

例えば、1 つのテストに 30 秒かかる場合、100 個のテストを実行すると 50 分必要です。これでは Pull Request のたびに長時間待つことになり、開発効率が著しく低下しますね。

リソースの非効率な利用

GitHub Actions には無料プランでも月 2,000 分の実行時間が提供されます。しかし、非効率な設定では、すぐに上限に達してしまうでしょう。

#項目最適化前最適化後改善率
1依存関係インストール4 分30 秒87%削減
2ブラウザインストール2 分10 秒92%削減
3テスト実行(100 件)50 分10 分80%削減

これらの課題を解決するために、キャッシュ最適化と並列戦略が必要になります。

解決策

キャッシュ戦略による高速化

GitHub Actions のキャッシュ機能を活用することで、繰り返しダウンロードする必要がなくなります。

キャッシュすべき対象は以下の 3 つです。

Node modules のキャッシュ

node_modules ディレクトリをキャッシュすることで、yarn install の実行時間を大幅に短縮できます。キャッシュキーには package.jsonyarn.lock のハッシュ値を使用し、依存関係が変更された場合のみ再インストールされるようにします。

Yarn キャッシュディレクトリ

Yarn 自体もキャッシュディレクトリを持っています。~​/​.yarn​/​cache をキャッシュすることで、パッケージのダウンロード時間をさらに短縮できるでしょう。

Playwright ブラウザバイナリ

Playwright のブラウザバイナリは ~​/​.cache​/​ms-playwright に保存されます。このディレクトリをキャッシュすることで、毎回のブラウザダウンロードを回避できます。

以下の図は、キャッシュを活用した最適化フローを示しています。

mermaidflowchart TD
  start["ワークフロー<br/>開始"] --> cache_check["キャッシュ<br/>確認"]
  cache_check -->|キャッシュ<br/>ヒット| restore["キャッシュ<br/>復元"]
  cache_check -->|キャッシュ<br/>ミス| install["新規<br/>インストール"]
  restore --> test["テスト<br/>実行"]
  install --> save["キャッシュ<br/>保存"]
  save --> test
  test --> finish["完了"]

  style restore fill:#ccffcc
  style test fill:#ccffcc

並列実行による時間短縮

GitHub Actions のマトリックス戦略を使用することで、複数のジョブを並列実行できます。

シャーディング戦略

Playwright には組み込みのシャーディング機能があります。テストを複数のグループに分割し、それぞれ異なるジョブで実行することで、全体の実行時間を削減できますね。

例えば、100 個のテストを 5 つのシャードに分割すれば、理論上は実行時間を 1/5 に短縮できます。

ブラウザ別並列実行

異なるブラウザでのテストも並列実行できます。Chromium、Firefox、WebKit のテストを同時に実行することで、クロスブラウザテストの時間を大幅に短縮できるでしょう。

リトライとタイムアウト設定

E2E テストは、ネットワークの状態やタイミングによって不安定になることがあります。

適切なリトライ設定とタイムアウトを設定することで、一時的な問題による失敗を回避し、テストの信頼性を高められます。

具体例

それでは、実際のコード例を見ながら、具体的な実装方法を解説していきます。

プロジェクトの初期設定

まず、Playwright をプロジェクトに導入します。

Playwright のインストール

以下のコマンドで Playwright をインストールしましょう。

bash# Playwrightのインストール
yarn add -D @playwright/test

# ブラウザのインストール
yarn playwright install

Playwright 設定ファイルの作成

playwright.config.ts を作成し、基本的な設定を行います。

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

/**
 * Playwright設定ファイル
 * テストの実行設定、ブラウザ設定、リトライ設定などを定義
 */
export default defineConfig({
  // テストファイルのディレクトリ
  testDir: './tests',

  // 並列実行の最大ワーカー数
  fullyParallel: true,

この設定により、テストがローカルでも CI 環境でも一貫して実行できます。

typescript  // CI環境での設定
  forbidOnly: !!process.env.CI,

  // リトライ回数(CI環境では2回)
  retries: process.env.CI ? 2 : 0,

  // 並列ワーカー数
  workers: process.env.CI ? 1 : undefined,
});

プロジェクト設定(ブラウザ指定)

複数のブラウザでテストを実行する設定を追加します。

typescript  // ブラウザ設定
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
typescript    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

GitHub Actions ワークフローの基本構成

次に、GitHub Actions のワークフローファイルを作成します。.github​/​workflows​/​playwright.yml を作成しましょう。

ワークフローのトリガー設定

ワークフローがいつ実行されるかを定義します。

yaml# Playwrightテストワークフロー
name: Playwright Tests

# トリガー設定
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

プッシュと Pull Request 時に自動実行されるよう設定しています。

環境変数の定義

ワークフロー全体で使用する環境変数を定義します。

yaml# 環境変数
env:
  NODE_VERSION: '20'
  PLAYWRIGHT_VERSION: '1.40.0'

バージョンを環境変数化することで、更新が容易になりますね。

キャッシュ最適化の実装

ここからが重要なポイントです。キャッシュを活用して実行時間を短縮します。

Node modules キャッシュ

Yarn の依存関係をキャッシュする設定を追加します。

yamljobs:
  test:
    runs-on: ubuntu-latest
    steps:
      # リポジトリのチェックアウト
      - name: Checkout code
        uses: actions/checkout@v4

      # Node.jsのセットアップ
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
yaml# Yarnキャッシュの復元
- name: Get yarn cache directory path
  id: yarn-cache-dir-path
  run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT

- name: Cache yarn dependencies
  uses: actions/cache@v3
  with:
    path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
    key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
    restore-keys: |
      ${{ runner.os }}-yarn-

yarn.lock のハッシュ値をキーにすることで、依存関係が変更された場合のみキャッシュが無効化されます。

Playwright ブラウザキャッシュ

Playwright のブラウザバイナリをキャッシュします。

yaml# Playwrightブラウザのキャッシュ
- name: Cache Playwright browsers
  uses: actions/cache@v3
  id: playwright-cache
  with:
    path: ~/.cache/ms-playwright
    key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
    restore-keys: |
      ${{ runner.os }}-playwright-

Playwright のバージョンをキーに含めることで、バージョンアップ時に適切に再ダウンロードされます。

依存関係のインストール

キャッシュを活用した依存関係のインストールを行います。

yaml# 依存関係のインストール
- name: Install dependencies
  run: yarn install --frozen-lockfile

# Playwrightブラウザのインストール(キャッシュミス時のみ)
- name: Install Playwright browsers
  if: steps.playwright-cache.outputs.cache-hit != 'true'
  run: yarn playwright install --with-deps

cache-hit の結果を確認し、キャッシュがない場合のみブラウザをインストールすることで、時間を節約できますね。

並列戦略の実装

次に、テストを並列実行する設定を追加します。

マトリックス戦略によるシャーディング

以下の図は、シャーディングによる並列実行の仕組みを示しています。

mermaidflowchart TD
  tests["全テスト<br/>100件"] --> shard1["Shard 1<br/>20件"]
  tests --> shard2["Shard 2<br/>20件"]
  tests --> shard3["Shard 3<br/>20件"]
  tests --> shard4["Shard 4<br/>20件"]
  tests --> shard5["Shard 5<br/>20件"]

  shard1 --> worker1["Worker 1"]
  shard2 --> worker2["Worker 2"]
  shard3 --> worker3["Worker 3"]
  shard4 --> worker4["Worker 4"]
  shard5 --> worker5["Worker 5"]

  worker1 --> result["テスト<br/>完了"]
  worker2 --> result
  worker3 --> result
  worker4 --> result
  worker5 --> result

実際の設定は以下のようになります。

yamljobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        # 5つのシャードに分割
        shard: [1, 2, 3, 4, 5]

fail-fast: false により、1 つのシャードが失敗しても他のシャードは継続実行されます。

シャード別テスト実行

各シャードでテストを実行するステップを追加します。

yamlsteps:
  # (前述のキャッシュ設定などを含む)

  # シャード別テスト実行
  - name: Run Playwright tests
    run: yarn playwright test --shard=${{ matrix.shard }}/5

--shard オプションにより、テストが自動的に分割されて実行されます。

ブラウザ別並列実行

さらに高度な並列化として、ブラウザごとにも並列実行できます。

yamljobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        # シャードとブラウザの組み合わせ
        shard: [1, 2, 3, 4, 5]
        browser: [chromium, firefox, webkit]
yamlsteps:
  # テスト実行(ブラウザとシャード指定)
  - name: Run Playwright tests
    run: |
      yarn playwright test \
        --project=${{ matrix.browser }} \
        --shard=${{ matrix.shard }}/5

この設定により、5 シャード × 3 ブラウザ = 15 個のジョブが並列実行されます。

テスト結果とアーティファクトの保存

テスト実行後、結果を保存して後から確認できるようにします。

テストレポートの生成

Playwright は自動的に HTML レポートを生成できます。

yaml# テスト実行
- name: Run Playwright tests
  run: yarn playwright test --shard=${{ matrix.shard }}/5

# テスト失敗時のスクリーンショット保存
- name: Upload test results
  if: always()
  uses: actions/upload-artifact@v3
  with:
    name: playwright-report-${{ matrix.shard }}
    path: playwright-report/
    retention-days: 30

if: always() により、テストが失敗した場合でもレポートがアップロードされます。

トレースファイルの保存

デバッグに役立つトレースファイルも保存しましょう。

yaml# トレースファイルの保存
- name: Upload trace files
  if: failure()
  uses: actions/upload-artifact@v3
  with:
    name: playwright-traces-${{ matrix.shard }}
    path: test-results/
    retention-days: 7

失敗時のみトレースを保存することで、ストレージを節約できますね。

完全なワークフロー例

これまでの設定を統合した完全なワークフローファイルは以下のようになります。

yamlname: Playwright Tests

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

env:
  NODE_VERSION: '20'
  PLAYWRIGHT_VERSION: '1.40.0'

jobs:
  test:
    runs-on: ubuntu-latest
yamlstrategy:
  fail-fast: false
  matrix:
    shard: [1, 2, 3, 4, 5]

steps:
  - name: Checkout code
    uses: actions/checkout@v4

  - name: Setup Node.js
    uses: actions/setup-node@v4
    with:
      node-version: ${{ env.NODE_VERSION }}
yaml- name: Get yarn cache directory path
  id: yarn-cache-dir-path
  run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT

- name: Cache yarn dependencies
  uses: actions/cache@v3
  with:
    path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
    key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
    restore-keys: |
      ${{ runner.os }}-yarn-
yaml- name: Cache Playwright browsers
  uses: actions/cache@v3
  id: playwright-cache
  with:
    path: ~/.cache/ms-playwright
    key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}

- name: Install dependencies
  run: yarn install --frozen-lockfile
yaml- name: Install Playwright browsers
  if: steps.playwright-cache.outputs.cache-hit != 'true'
  run: yarn playwright install --with-deps

- name: Run Playwright tests
  run: yarn playwright test --shard=${{ matrix.shard }}/5
yaml- name: Upload test results
  if: always()
  uses: actions/upload-artifact@v3
  with:
    name: playwright-report-${{ matrix.shard }}
    path: playwright-report/
    retention-days: 30

この設定により、高速で信頼性の高いテスト環境が実現できます。

サンプルテストの作成

最後に、実際のテストコード例を見てみましょう。

基本的なテストケース

tests​/​example.spec.ts を作成します。

typescriptimport { test, expect } from '@playwright/test';

/**
 * ホームページのテスト
 * タイトルとメインコンテンツの表示を確認
 */
test('ホームページが正しく表示される', async ({ page }) => {
  // ページにアクセス
  await page.goto('https://example.com');
typescript  // タイトルの確認
  await expect(page).toHaveTitle(/Example Domain/);

  // メインコンテンツの確認
  const heading = page.locator('h1');
  await expect(heading).toBeVisible();
});

フォーム送信のテスト

より実践的なフォーム送信のテストです。

typescript/**
 * お問い合わせフォームのテスト
 * フォーム入力から送信完了までの流れを検証
 */
test('お問い合わせフォームが送信できる', async ({ page }) => {
  // お問い合わせページにアクセス
  await page.goto('https://example.com/contact');

  // フォームに入力
  await page.fill('input[name="name"]', '山田太郎');
  await page.fill('input[name="email"]', 'taro@example.com');
typescript  // メッセージを入力
  await page.fill('textarea[name="message"]', 'お問い合わせ内容');

  // 送信ボタンをクリック
  await page.click('button[type="submit"]');

  // 完了メッセージの確認
  await expect(page.locator('.success-message')).toBeVisible();
});

これらのテストが、設定した並列戦略とキャッシュにより、高速に実行されます。

まとめ

本記事では、Playwright と GitHub Actions を組み合わせた E2E テスト環境の構築方法を、基本から最適化まで解説しました。

重要なポイントをまとめます。

キャッシュ最適化の効果

Node modules と Playwright ブラウザのキャッシュにより、セットアップ時間を大幅に短縮できます。特に、yarn.lock と Playwright バージョンをキーにしたキャッシュ戦略が効果的でしょう。

並列戦略による高速化

シャーディングとマトリックス戦略を組み合わせることで、テスト実行時間を理論上 1/5 以下に短縮できます。プロジェクトの規模に応じてシャード数を調整することが重要ですね。

実運用での注意点

リトライ設定とタイムアウト設定により、テストの信頼性を高められます。また、テスト結果とトレースファイルの保存により、問題発生時のデバッグが容易になります。

これらの設定を適切に組み合わせることで、開発速度を落とすことなく、高品質な Web アプリケーションを継続的に提供できるでしょう。

ぜひ、あなたのプロジェクトでも Playwright × GitHub Actions の導入を検討してみてください。最初は小さく始めて、徐々に最適化していくアプローチがおすすめです。

関連リンク