T-CREATOR

Playwright の並列実行で大量テストを高速消化!

Playwright の並列実行で大量テストを高速消化!

Playwright テストの実行時間に悩まされていませんか。テストケースが増えるたびに、実行完了を待つ時間が長くなっていく状況を解決する強力な手法があります。

大量のテストケースを効率的に処理する並列実行の威力をご紹介します。この記事では、Playwright の並列実行機能を使って、テスト実行時間を劇的に短縮する実践的な方法をお伝えします。

背景

E2E テストの実行時間が長期化する問題

現代の Web アプリケーション開発では、品質保証のために E2E テストが欠かせません。しかし、アプリケーションが成長するにつれて、テストケースも増加し続けています。

単体テストであれば数千件実行しても数分で完了しますが、E2E テストは 1 件あたり数秒から数十秒かかるため、100 件のテストでも 10 分以上の実行時間が必要になることがあります。

テストの実行時間が長くなると、開発者のフィードバックループが遅くなり、開発効率が大幅に低下してしまいます。

CI/CD パイプラインでのボトルネック

継続的インテグレーション環境では、テスト実行時間がパイプライン全体の実行時間を左右します。特に以下のような問題が発生しがちです。

  • プルリクエストのマージまでに長時間を要する
  • 並行して動作する複数のパイプラインでリソース競合が発生
  • テスト失敗時の再実行に時間がかかり、開発スピードが低下

テスト実行時間の長期化は、チーム全体の生産性に直接影響を与える重要な課題となっています。

テスト数増加に伴う課題

機能追加やバグ修正のたびにテストケースが追加されるため、テスト実行時間は比例的に増加します。この線形的な増加は以下の問題を引き起こします。

mermaidflowchart LR
  tests[テスト数] -->|線形増加| time[実行時間]
  time -->|影響| dev[開発効率]
  time -->|影響| ci[CI/CD速度]
  dev -->|低下| quality[品質保証]
  ci -->|低下| deploy[デプロイ頻度]

図で示すように、テスト数の増加が開発プロセス全体に連鎖的な影響を与えることがわかります。

課題

単一スレッドでの逐次実行の限界

従来の Playwright テスト実行では、テストが順次実行されるため、CPU やネットワークリソースが十分に活用されていません。

以下のような状況が典型的な問題です。

typescript// 従来の逐次実行パターン
test('ユーザー登録テスト', async ({ page }) => {
  // 5秒かかるテスト
});

test('ログインテスト', async ({ page }) => {
  // 3秒かかるテスト  
});

test('商品検索テスト', async ({ page }) => {
  // 7秒かかるテスト
});
// 合計: 15秒(逐次実行)

この例では、各テストが独立しているにも関わらず、合計 15 秒の実行時間が必要になります。

テスト実行時間とチーム生産性の関係

テスト実行時間の長期化は、開発チームの作業パターンに深刻な影響を与えます。

実行時間開発者の行動パターン生産性への影響
1-2分テスト完了を待機高い集中力を維持
5-10分他の作業に移行コンテキストスイッチ発生
15分以上完全に別作業へ大幅な効率低下

実行時間が長くなるほど、開発者の集中力が分散し、全体的な生産性が低下してしまいます。

リソース使用率の最適化問題

現代のマシンはマルチコア CPU を搭載していますが、逐次実行では 1 つのコアしか使用されません。

mermaidflowchart TD
  subgraph "逐次実行"
    core1[CPU Core 1: 100%使用]
    core2[CPU Core 2: 0%使用]
    core3[CPU Core 3: 0%使用]
    core4[CPU Core 4: 0%使用]
  end
  
  subgraph "並列実行"
    pcore1[CPU Core 1: 75%使用]
    pcore2[CPU Core 2: 75%使用]
    pcore3[CPU Core 3: 75%使用]
    pcore4[CPU Core 4: 75%使用]
  end

利用可能なリソースを最大限活用することで、大幅な実行時間短縮が期待できます。

解決策

Playwright の並列実行機能

Playwright には強力な並列実行機能が組み込まれており、複数のワーカープロセスでテストを同時実行できます。

基本的な並列実行の仕組み

Playwright の並列実行は以下の仕組みで動作します。

mermaidflowchart LR
  main[メインプロセス] -->|テスト配布| worker1[ワーカー1]
  main -->|テスト配布| worker2[ワーカー2]
  main -->|テスト配布| worker3[ワーカー3]
  worker1 -->|結果収集| main
  worker2 -->|結果収集| main
  worker3 -->|結果収集| main

各ワーカーは独立したブラウザインスタンスを持ち、テスト間の干渉を防ぎます。

playwright.config.ts での設定

並列実行を有効にするための基本設定は非常にシンプルです。

typescript// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  // 並列実行のワーカー数を指定
  workers: 4,
  
  // テスト実行の設定
  use: {
    baseURL: 'http://localhost:3000',
  },
});

この設定により、4 つのワーカーでテストが並列実行されます。

ワーカー数の最適化設定

ワーカー数の選択は、実行環境のリソースとテストの特性を考慮して決定する必要があります。

CPU コア数に基づく設定

typescript// CPU コア数に基づく動的設定
export default defineConfig({
  workers: process.env.CI ? 2 : '50%',
});

この設定では、CI 環境では 2 ワーカー、ローカル環境では CPU コア数の 50% を使用します。

環境別の最適化

typescript// 環境別の詳細設定
export default defineConfig({
  workers: (() => {
    if (process.env.CI) {
      // CI環境: リソース制約を考慮
      return 2;
    } else if (process.env.NODE_ENV === 'development') {
      // 開発環境: 高速化重視
      return '75%';
    } else {
      // 本番テスト: 安定性重視
      return 1;
    }
  })(),
});

環境ごとに異なる戦略を採用することで、最適なパフォーマンスを実現できます。

テスト分散戦略

効果的な並列実行のためには、テストの分散方法も重要です。

ファイル単位での分散

Playwright はデフォルトでファイル単位でテストを分散します。

typescript// test1.spec.ts - ワーカー1で実行
test('テスト1-1', async ({ page }) => { /* */ });
test('テスト1-2', async ({ page }) => { /* */ });

// test2.spec.ts - ワーカー2で実行  
test('テスト2-1', async ({ page }) => { /* */ });
test('テスト2-2', async ({ page }) => { /* */ });

この方法では、関連するテストを同じワーカーで実行することで、セットアップの効率化が図れます。

テスト実行時間の均等化

ファイルごとの実行時間を均等化することで、並列実行の効果を最大化できます。

typescript// 長時間テストの分割例

// 分割前: heavy-tests.spec.ts (15分)
// 分割後:
// user-registration.spec.ts (5分)
// payment-flow.spec.ts (5分)  
// admin-functions.spec.ts (5分)

テストファイルを適切に分割することで、ワーカー間の負荷バランスが改善されます。

具体例

基本的な並列実行設定

実際のプロジェクトで並列実行を導入する手順を段階的に説明します。

ステップ 1: 現在の実行時間を測定

まず、現在のテスト実行時間を正確に把握します。

bash# 逐次実行での時間測定
yarn playwright test --reporter=list

実行結果の例:

sqlRunning 50 tests using 1 worker
  ✓ test1.spec.ts:3:1 › ユーザー登録テスト (5.2s)
  ✓ test2.spec.ts:3:1 › ログインテスト (3.1s)
  ...
  
  50 passed (12m 34s)

ステップ 2: 並列実行の有効化

playwright.config.ts に並列実行設定を追加します。

typescript// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  
  // 並列実行設定
  workers: process.env.CI ? 2 : 4,
  
  // 並列実行時の設定
  fullyParallel: true,
  
  // テスト失敗時の動作
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
  },
});

重要な設定項目の説明:

  • workers: 並列実行するワーカー数
  • fullyParallel: ファイル内のテストも並列実行
  • retries: 失敗したテストの再実行回数

ステップ 3: 実行時間の比較

並列実行を有効にして時間を測定します。

bash# 並列実行での時間測定  
yarn playwright test --reporter=list --workers=4

実行結果の例:

sqlRunning 50 tests using 4 workers
  ✓ test1.spec.ts:3:1 › ユーザー登録テスト (5.2s)
  ✓ test2.spec.ts:3:1 › ログインテスト (3.1s)
  ...
  
  50 passed (3m 45s)

この例では、12 分 34 秒から 3 分 45 秒へと、約 70% の時間短縮を実現しています。

プロジェクト別並列実行

大規模なプロジェクトでは、複数のプロジェクト設定を使って並列実行を最適化できます。

プロジェクト設定の分離

typescript// playwright.config.ts
export default defineConfig({
  projects: [
    {
      name: 'chrome-desktop',
      use: { ...devices['Desktop Chrome'] },
      testDir: './tests/desktop',
    },
    {
      name: 'mobile-safari',
      use: { ...devices['iPhone 12'] },
      testDir: './tests/mobile',
    },
    {
      name: 'api-tests',
      testDir: './tests/api',
      use: { baseURL: 'http://localhost:3001/api' },
    },
  ],
  
  // プロジェクト別ワーカー設定
  workers: '50%',
});

プロジェクト単位での実行

bash# 特定プロジェクトのみ実行
yarn playwright test --project=chrome-desktop

# 複数プロジェクトの並列実行
yarn playwright test --project=chrome-desktop --project=mobile-safari

CI 環境での最適化事例

GitHub Actions での設定例

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

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

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: 18
        
    - name: Install dependencies
      run: yarn install --frozen-lockfile
      
    - name: Install Playwright Browsers
      run: yarn playwright install --with-deps
      
    - name: Run Playwright tests
      run: yarn playwright test --shard=${{ matrix.shard }}/4
      
    - name: Upload test results
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: playwright-report-${{ matrix.shard }}
        path: playwright-report/

この設定により、テストを 4 つのシャードに分割して並列実行し、実行時間を大幅に短縮できます。

シャーディングによる最適化

シャーディング機能を使用することで、CI 環境でのスケーラビリティを向上させられます。

mermaidflowchart TD
  main[メインジョブ] -->|分割| shard1[シャード1: テスト1-12]
  main -->|分割| shard2[シャード2: テスト13-25]
  main -->|分割| shard3[シャード3: テスト26-37]
  main -->|分割| shard4[シャード4: テスト38-50]
  
  shard1 -->|結果| collect[結果収集]
  shard2 -->|結果| collect
  shard3 -->|結果| collect
  shard4 -->|結果| collect

シャーディングにより、テスト実行時間を理論上 1/4 に短縮できます。

Docker での並列実行

dockerfile# Dockerfile.playwright
FROM mcr.microsoft.com/playwright:v1.40.0-focal

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

# 並列実行用の設定
ENV PLAYWRIGHT_WORKERS=4
ENV CI=true

CMD ["npm", "run", "test:ci"]

Docker 環境でも並列実行を活用することで、一貫したパフォーマンスを実現できます。

実際の改善事例

あるプロジェクトでの具体的な改善結果をご紹介します。

項目改善前改善後改善率
ローカル実行時間25分8分68%短縮
CI実行時間35分12分66%短縮
ワーカー数144倍並列
テスト数150件150件同じ

この改善により、開発チームの生産性が大幅に向上し、より迅速なフィードバックループを実現できました。

まとめ

Playwright の並列実行機能は、テスト実行時間の大幅な短縮を実現する強力なソリューションです。

適切なワーカー数の設定とテスト分散戦略により、従来の 3 分の 1 から 4 分の 1 の実行時間でテストを完了できます。特に大規模なプロジェクトや CI/CD 環境では、その効果は絶大です。

並列実行を導入する際は、段階的なアプローチを取り、環境に応じた最適化を行うことが重要です。リソース使用率の改善とテスト実行時間の短縮により、開発チーム全体の生産性向上を実現しましょう。

継続的な測定と改善を通じて、さらなるパフォーマンス向上を目指していくことをお勧めします。

関連リンク