T-CREATOR

Playwright MCP と GitHub Actions で全自動 E2E パイプライン構築

Playwright MCP と GitHub Actions で全自動 E2E パイプライン構築

現代の Web アプリケーション開発において、品質保証は単なる「あればいいもの」ではなく、ユーザー体験を左右する重要な要素となっています。特に End-to-End(E2E)テストは、実際のユーザー操作を模倣することで、リリース前に致命的なバグを発見できる強力な手段です。しかし、従来の E2E テストには実行時間の長さや環境依存性などの課題があり、多くの開発チームが頭を悩ませているのが現状でしょう。

本記事では、Microsoft の Playwright と Model Context Protocol(MCP)、そして GitHub Actions を組み合わせることで、これらの課題を解決し、真の意味での「全自動 E2E パイプライン」を構築する方法をご紹介します。この組み合わせにより、インテリジェントで効率的、そして信頼性の高いテスト環境を実現できるようになります。

背景 - E2E テスト自動化の重要性と現状の課題

なぜ E2E テストが重要なのか

モダンな Web アプリケーションは、フロントエンド、バックエンド、データベース、外部 API など複数のコンポーネントが複雑に連携して動作しています。単体テストや統合テストだけでは、これらすべての要素が正しく連携しているかを確認することは困難です。

E2E テストは、実際のユーザーがブラウザで操作する流れをそのまま自動化することで、アプリケーション全体の動作を検証できます。例えば、ユーザー登録からログイン、商品購入まで一連の流れを自動でテストすることで、リリース前に重大なバグを発見することが可能になります。

現在の E2E テスト環境が抱える課題

多くの開発チームが E2E テストで直面している課題は以下の通りです:

#課題項目具体的な問題
1実行時間テスト実行に数時間かかることがある
2環境依存性ローカル環境と CI 環境での動作差異
3メンテナンス性UI 変更によるテストの頻繁な修正
4並列実行複数テストの同時実行時の競合
5レポートテスト結果の可視化と分析の困難さ

これらの課題により、E2E テストは「重要だが扱いにくいもの」として敬遠される傾向にあります。

課題 - 従来の E2E パイプラインの限界

スケーラビリティの問題

従来の E2E テストパイプラインは、テストケースが増加するにつれて実行時間が線形に増加していきます。100 個のテストケースが 10 分で完了していても、1000 個になると 100 分かかるという単純な計算になってしまいます。

この問題を解決するために並列実行を導入しても、今度はテスト間の依存関係やリソース競合といった新たな課題が生まれます。例えば、同一のテストデータを複数のテストが同時に操作することで、予期しないテスト失敗が発生することがあります。

環境構築の複雑さ

E2E テストを実行するためには、アプリケーション本体だけでなく、データベース、外部 API、認証システムなど多くのコンポーネントを準備する必要があります。ローカル環境では動作するテストが、CI 環境では失敗するという経験をされた方も多いのではないでしょうか。

特に以下のようなエラーに遭遇することが頻繁にあります:

javascriptError: Target page, context or browser has been closed
Error: Timeout 30000ms exceeded
Error: Page crashed!

テストの脆弱性

UI の小さな変更でもテストが失敗することがあり、開発チームはテストの修正に多くの時間を費やすことになります。この「脆いテスト」問題は、E2E テストの価値を大きく損なう要因となっています。

解決策 - Playwright MCP + GitHub Actions の組み合わせ

なぜこの組み合わせが効果的なのか

Playwright、MCP、GitHub Actions の組み合わせは、それぞれの強みを活かしながら、従来の課題を解決できる理想的なソリューションです。

Playwrightは、Microsoft が開発した次世代の E2E テストフレームワークで、複数ブラウザでの並列実行、強力な待機機能、詳細なデバッグ機能を提供します。

**MCP(Model Context Protocol)**は、LLM との連携を可能にするプロトコルで、テストの自動生成や結果分析において人工知能の力を活用できます。

GitHub Actionsは、クラウドベースの CI/CD プラットフォームで、スケーラブルで信頼性の高い実行環境を提供します。

アーキテクチャの全体像

この 3 つの技術を組み合わせることで、以下のような全自動 E2E パイプラインを構築できます:

  1. コード変更の検知 - GitHub Actions が自動的にトリガー
  2. 環境構築 - Docker コンテナによる一貫した環境
  3. テスト実行 - Playwright による並列テスト実行
  4. 結果分析 - MCP を活用した自動分析とレポート生成
  5. フィードバック - プルリクエストへの自動コメント

環境構築 - 開発環境のセットアップ

必要な技術要件

まず、開発環境に必要な技術要件を確認しましょう。この記事では、モダンな JavaScript/TypeScript 環境を前提としています。

#技術要素バージョン用途
1Node.js18.x 以上ランタイム環境
2Yarn1.22.x 以上パッケージ管理
3TypeScript5.x 以上型安全な開発
4Docker20.x 以上コンテナ化

プロジェクトの初期設定

新しいプロジェクトを作成し、必要な依存関係をインストールしていきます。以下のコマンドで環境を整備しましょう。

bash# プロジェクトディレクトリの作成
mkdir e2e-automation-pipeline
cd e2e-automation-pipeline

# package.jsonの初期化
yarn init -y

# TypeScriptの設定
yarn add -D typescript @types/node
npx tsc --init

この初期設定により、TypeScript ベースの開発環境が準備されます。TypeScript を使用することで、型安全性が確保され、IDE 上での補完機能も充実するため、開発効率が大幅に向上します。

Playwright のインストールと設定

続いて、Playwright をプロジェクトに追加します。Playwright は、複数のブラウザエンジンを同梱しているため、初回インストール時には時間がかかる場合があります。

bash# Playwrightとテスト関連パッケージのインストール
yarn add -D @playwright/test
yarn add -D @types/jest

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

インストール完了後、Playwright の設定ファイルを作成します。この設定ファイルは、テストの実行方法や並列度、レポート形式などを定義する重要なファイルです。

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [
    ['html'],
    ['json', { outputFile: 'test-results.json' }],
  ],
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
  ],
  webServer: {
    command: 'yarn start',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

この設定により、Chrome、Firefox 両方でのテスト実行、失敗時のスクリーンショット自動保存、詳細なレポート生成が可能になります。

Playwright MCP の実装 - MCP サーバーの構築

MCP サーバーの基本構造

Model Context Protocol(MCP)は、LLM との連携を可能にする強力なプロトコルです。E2E テストにおいては、テスト結果の分析やテストケースの自動生成において、その威力を発揮します。

まず、MCP サーバーの基本的な構造を理解しましょう。MCP サーバーは、LLM からのリクエストを受け取り、Playwright テストの実行や結果分析を行う役割を担います。

typescript// src/mcp-server.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

class PlaywrightMCPServer {
  private server: Server;

  constructor() {
    this.server = new Server(
      {
        name: 'playwright-e2e-server',
        version: '1.0.0',
      },
      {
        capabilities: {
          tools: {
            listChanged: true,
          },
        },
      }
    );

    this.setupTools();
  }

  private setupTools() {
    // テスト実行ツールの登録
    this.server.setRequestHandler(
      ListToolsRequestSchema,
      async () => ({
        tools: [
          {
            name: 'run_e2e_tests',
            description: 'Execute Playwright E2E tests',
            inputSchema: z.object({
              testPattern: z.string().optional(),
              browser: z
                .enum(['chromium', 'firefox', 'webkit'])
                .optional(),
              parallel: z.boolean().optional(),
            }),
          },
        ],
      })
    );
  }
}

このサーバー構造により、LLM が Playwright テストを直接制御できるようになります。

テスト実行機能の実装

MCP サーバーにテスト実行機能を追加します。この機能により、LLM からの指示に基づいて動的にテストを実行できるようになります。

typescript// src/test-executor.ts
import { execSync } from 'child_process';
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';

export class TestExecutor {
  async executeTests(params: {
    testPattern?: string;
    browser?: string;
    parallel?: boolean;
  }): Promise<TestResult> {
    try {
      const command = this.buildTestCommand(params);
      const output = execSync(command, {
        encoding: 'utf-8',
        cwd: process.cwd(),
      });

      return this.parseTestOutput(output);
    } catch (error) {
      // よく発生するエラーパターンをハンドリング
      if (
        error.message.includes(
          'Target page, context or browser has been closed'
        )
      ) {
        throw new Error(
          'BROWSER_CLOSED: ブラウザが予期せず終了しました'
        );
      }

      if (error.message.includes('Timeout')) {
        throw new Error(
          'TEST_TIMEOUT: テストがタイムアウトしました'
        );
      }

      throw new Error(
        `TEST_EXECUTION_FAILED: ${error.message}`
      );
    }
  }

  private buildTestCommand(params: any): string {
    let command = 'npx playwright test';

    if (params.testPattern) {
      command += ` ${params.testPattern}`;
    }

    if (params.browser) {
      command += ` --project=${params.browser}`;
    }

    if (params.parallel) {
      command += ' --workers=4';
    }

    return command;
  }
}

このテスト実行機能では、一般的な Playwright エラーを適切にハンドリングし、わかりやすいエラーメッセージを提供しています。

結果分析機能の実装

テスト結果を自動分析し、有用な洞察を提供する機能を実装します。この機能により、テスト失敗の原因を素早く特定できるようになります。

typescript// src/result-analyzer.ts
interface TestResult {
  passed: number;
  failed: number;
  skipped: number;
  duration: number;
  failures: TestFailure[];
}

interface TestFailure {
  testName: string;
  error: string;
  screenshot?: string;
  trace?: string;
}

export class ResultAnalyzer {
  analyzeResults(results: TestResult): AnalysisReport {
    const report: AnalysisReport = {
      summary: this.generateSummary(results),
      recommendations:
        this.generateRecommendations(results),
      errorPatterns: this.identifyErrorPatterns(results),
    };

    return report;
  }

  private identifyErrorPatterns(
    results: TestResult
  ): ErrorPattern[] {
    const patterns: ErrorPattern[] = [];

    results.failures.forEach((failure) => {
      // よくあるエラーパターンの分析
      if (
        failure.error.includes(
          'locator.click: Target closed'
        )
      ) {
        patterns.push({
          type: 'BROWSER_CRASH',
          description:
            'ブラウザが予期せず終了している可能性があります',
          suggestion:
            'ブラウザの起動オプションを確認してください',
        });
      }

      if (
        failure.error.includes('Timeout 30000ms exceeded')
      ) {
        patterns.push({
          type: 'ELEMENT_NOT_FOUND',
          description: '要素の待機がタイムアウトしています',
          suggestion:
            'セレクターの見直しまたは待機時間の調整を検討してください',
        });
      }
    });

    return patterns;
  }
}

この分析機能により、テスト失敗の根本原因を素早く特定し、適切な対策を提案できるようになります。

GitHub Actions 設定 - ワークフロー構築

基本的なワークフロー設定

GitHub Actions を使用して、コード変更時に自動的に E2E テストを実行するワークフローを構築します。以下のワークフロー設定は、効率的で信頼性の高いテスト実行を可能にします。

yaml# .github/workflows/e2e-tests.yml
name: E2E Tests with Playwright MCP

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

env:
  NODE_VERSION: '18'
  YARN_CACHE_FOLDER: ~/.yarn

jobs:
  e2e-tests:
    timeout-minutes: 60
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        browser: [chromium, firefox]

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'yarn'

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Install Playwright Browsers
        run: npx playwright install --with-deps ${{ matrix.browser }}

このワークフロー設定では、複数のブラウザでの並列実行と、依存関係のキャッシュによる実行時間の短縮を実現しています。

Docker 環境での実行設定

一貫性のあるテスト環境を確保するため、Docker コンテナ内でのテスト実行も設定します。これにより、開発環境と CI 環境での差異を最小限に抑えることができます。

yaml- name: Start application services
  run: |
    docker-compose up -d
    # アプリケーションの起動を待機
    timeout 60 bash -c 'until curl -f http://localhost:3000/health; do sleep 2; done'

- name: Run E2E tests
  run: |
    yarn playwright test --project=${{ matrix.browser }} --reporter=html,json
  env:
    CI: true
    PLAYWRIGHT_BROWSER: ${{ matrix.browser }}

- name: Upload test results
  uses: actions/upload-artifact@v4
  if: always()
  with:
    name: playwright-report-${{ matrix.browser }}
    path: |
      playwright-report/
      test-results.json
    retention-days: 30

エラーハンドリングとリトライ機能

E2E テストでは、ネットワークの一時的な問題や外部サービスの遅延などにより、偶発的な失敗が発生することがあります。以下の設定で、これらの問題に対応します。

yaml- name: Run E2E tests with retry
  uses: nick-fields/retry@v2
  with:
    timeout_minutes: 30
    max_attempts: 3
    retry_on: error
    command: |
      if ! yarn playwright test --project=${{ matrix.browser }}; then
        echo "テスト実行に失敗しました。リトライします..."
        exit 1
      fi

- name: Handle test failures
  if: failure()
  run: |
    echo "::error::E2Eテストが失敗しました"
    # 失敗時のデバッグ情報を収集
    docker-compose logs > docker-logs.txt
    yarn playwright show-trace test-results/ || true

- name: Upload failure artifacts
  if: failure()
  uses: actions/upload-artifact@v4
  with:
    name: failure-debug-${{ matrix.browser }}
    path: |
      docker-logs.txt
      test-results/
      screenshots/

この設定により、テスト失敗時に必要なデバッグ情報が自動的に収集され、問題の解決が迅速に行えるようになります。

パイプライン統合 - 全自動化の実現

MCP 統合による高度な自動化

MCP サーバーを GitHub Actions ワークフローに統合することで、テストの実行だけでなく、結果の分析と改善提案まで自動化できます。

yaml- name: Setup MCP Server
  run: |
    # MCPサーバーの起動
    node src/mcp-server.js &
    MCP_PID=$!
    echo "MCP_PID=$MCP_PID" >> $GITHUB_ENV

    # サーバーの起動確認
    timeout 30 bash -c 'until curl -f http://localhost:8080/health; do sleep 1; done'

- name: Execute tests via MCP
  run: |
    # MCPを通じてテスト実行
    curl -X POST http://localhost:8080/execute-tests \
      -H "Content-Type: application/json" \
      -d '{
        "testPattern": "tests/**/*.spec.ts",
        "browser": "${{ matrix.browser }}",
        "parallel": true
      }' > mcp-results.json

- name: Analyze results with AI
  run: |
    # AI分析の実行
    curl -X POST http://localhost:8080/analyze-results \
      -H "Content-Type: application/json" \
      -d @mcp-results.json > analysis-report.json

自動レポート生成と PR コメント

テスト結果を自動的に分析し、プルリクエストに分かりやすいコメントを投稿する機能を実装します。

typescript// scripts/generate-pr-comment.ts
import { readFileSync } from 'fs';
import { Octokit } from '@octokit/rest';

interface TestAnalysis {
  summary: {
    total: number;
    passed: number;
    failed: number;
    duration: number;
  };
  recommendations: string[];
  errorPatterns: ErrorPattern[];
}

async function generatePRComment() {
  const analysis: TestAnalysis = JSON.parse(
    readFileSync('analysis-report.json', 'utf-8')
  );

  const comment = `## 🎭 E2Eテスト結果

## 📊 テストサマリー
- **実行テスト数**: ${analysis.summary.total}
- **成功**: ${analysis.summary.passed} ✅
- **失敗**: ${analysis.summary.failed} ❌
- **実行時間**: ${Math.round(
    analysis.summary.duration / 1000
  )}秒

## 🔍 分析結果
${analysis.recommendations
  .map((rec) => `- ${rec}`)
  .join('\n')}

## ⚠️ 検出されたエラーパターン
${analysis.errorPatterns
  .map(
    (pattern) =>
      `- **${pattern.type}**: ${pattern.description}\n  💡 ${pattern.suggestion}`
  )
  .join('\n\n')}`;

  // GitHubAPIを使用してコメント投稿
  const octokit = new Octokit({
    auth: process.env.GITHUB_TOKEN,
  });

  await octokit.rest.issues.createComment({
    owner: process.env.GITHUB_REPOSITORY_OWNER!,
    repo: process.env.GITHUB_REPOSITORY!.split('/')[1],
    issue_number: parseInt(process.env.PR_NUMBER!),
    body: comment,
  });
}

この自動レポート機能により、開発者は一目でテスト結果を把握し、必要な対応を迅速に行えるようになります。

段階的デプロイメント連携

E2E テストの成功を条件とした段階的デプロイメントパイプラインも構築できます。

yamldeploy-staging:
  needs: e2e-tests
  if: success() && github.ref == 'refs/heads/develop'
  runs-on: ubuntu-latest

  steps:
    - name: Deploy to staging
      run: |
        echo "E2Eテストが成功したため、ステージング環境にデプロイします"
        # デプロイ処理

deploy-production:
  needs: e2e-tests
  if: success() && github.ref == 'refs/heads/main'
  runs-on: ubuntu-latest

  environment: production
  steps:
    - name: Deploy to production
      run: |
        echo "E2Eテストが成功したため、本番環境にデプロイします"
        # 本番デプロイ処理

運用とメンテナンス - 継続的な改善

パフォーマンス監視と最適化

E2E パイプラインを継続的に改善するため、パフォーマンス監視機能を実装します。テスト実行時間やリソース使用量を追跡し、ボトルネックを特定することで、より効率的なパイプラインを維持できます。

typescript// src/performance-monitor.ts
export class PerformanceMonitor {
  private metrics: PerformanceMetrics = {
    testDuration: [],
    memoryUsage: [],
    cpuUsage: [],
    networkRequests: [],
  };

  startMonitoring() {
    const startTime = Date.now();
    const startMemory = process.memoryUsage();

    return {
      stop: () => {
        const endTime = Date.now();
        const endMemory = process.memoryUsage();

        this.metrics.testDuration.push(endTime - startTime);
        this.metrics.memoryUsage.push(
          endMemory.heapUsed - startMemory.heapUsed
        );

        return this.generateReport();
      },
    };
  }

  generateReport(): PerformanceReport {
    return {
      averageDuration: this.calculateAverage(
        this.metrics.testDuration
      ),
      peakMemoryUsage: Math.max(
        ...this.metrics.memoryUsage
      ),
      recommendations:
        this.generateOptimizationRecommendations(),
    };
  }

  private generateOptimizationRecommendations(): string[] {
    const recommendations: string[] = [];

    const avgDuration = this.calculateAverage(
      this.metrics.testDuration
    );
    if (avgDuration > 300000) {
      // 5分超過
      recommendations.push(
        'テスト実行時間が長すぎます。並列度の調整を検討してください'
      );
    }

    const peakMemory = Math.max(
      ...this.metrics.memoryUsage
    );
    if (peakMemory > 1000000000) {
      // 1GB超過
      recommendations.push(
        'メモリ使用量が多すぎます。テストの分割を検討してください'
      );
    }

    return recommendations;
  }
}

テストケースの自動メンテナンス

UI の変更に伴うテストケースの更新を自動化することで、メンテナンスコストを大幅に削減できます。

typescript// src/test-maintenance.ts
export class TestMaintenance {
  async updateSelectors(
    testFile: string
  ): Promise<UpdateResult> {
    try {
      const content = readFileSync(testFile, 'utf-8');
      const ast = parseTypeScript(content);

      const updates: SelectorUpdate[] = [];

      // 古いセレクターパターンを検出
      ast.statements.forEach((statement) => {
        if (this.isObsoleteSelector(statement)) {
          const newSelector =
            this.generateModernSelector(statement);
          updates.push({
            old: statement.text,
            new: newSelector,
            line: statement.line,
          });
        }
      });

      // 自動更新の実行
      if (updates.length > 0) {
        await this.applyUpdates(testFile, updates);
        return {
          success: true,
          updatesApplied: updates.length,
        };
      }

      return { success: true, updatesApplied: 0 };
    } catch (error) {
      if (error.code === 'PARSE_ERROR') {
        throw new Error(
          'SYNTAX_ERROR: テストファイルの構文エラーです'
        );
      }
      throw new Error(
        `MAINTENANCE_FAILED: ${error.message}`
      );
    }
  }

  private isObsoleteSelector(statement: any): boolean {
    // 非推奨のセレクターパターンを検出
    const obsoletePatterns = [
      /page\.locator\('\.old-class'\)/,
      /page\.locator\('#deprecated-id'\)/,
      /page\.getByRole\('button', \{ name: 'Submit' \}\)/, // より安定したセレクターに変更
    ];

    return obsoletePatterns.some((pattern) =>
      pattern.test(statement.text)
    );
  }
}

障害対応とアラート設定

E2E パイプラインの障害を迅速に検知し、適切に対応するためのアラート機能を実装します。

typescript// src/alert-manager.ts
export class AlertManager {
  async checkSystemHealth(): Promise<HealthStatus> {
    const checks = [
      this.checkBrowserAvailability(),
      this.checkTestEnvironment(),
      this.checkExternalDependencies(),
    ];

    const results = await Promise.allSettled(checks);

    const failures = results
      .filter((result) => result.status === 'rejected')
      .map(
        (result) => (result as PromiseRejectedResult).reason
      );

    if (failures.length > 0) {
      await this.sendAlert(failures);
      return { healthy: false, issues: failures };
    }

    return { healthy: true, issues: [] };
  }

  private async sendAlert(issues: string[]) {
    const message = `🚨 E2Eパイプライン障害検知\n\n${issues.join(
      '\n'
    )}`;

    // Slack通知の例
    await fetch(process.env.SLACK_WEBHOOK_URL!, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: message,
        channel: '#alerts',
        username: 'E2E Pipeline Bot',
      }),
    });
  }
}

この包括的な監視とアラート機能により、問題を早期に発見し、サービスの安定性を維持できます。

まとめ

本記事では、Playwright MCP と GitHub Actions を組み合わせた全自動 E2E パイプラインの構築方法をご紹介しました。この組み合わせにより、従来の E2E テストが抱えていた課題を解決し、真の意味での自動化を実現できるようになります。

実現できた成果

#改善項目従来改善後
1実行時間数時間並列実行により 30 分以内
2環境一貫性環境依存の問題ありDocker による完全一致
3結果分析手動で時間がかかるAI による自動分析
4メンテナンス頻繁な手動修正自動更新とアラート

導入による価値

この全自動 E2E パイプラインの導入により、開発チームは以下の価値を得ることができます:

品質の向上 - 継続的で包括的なテストにより、バグの早期発見と品質向上を実現 開発効率の向上 - 手動テストからの解放により、開発により多くの時間を投入可能 安心感の獲得 - 自動化されたテストにより、リリースに対する不安を大幅に軽減 コスト削減 - テストメンテナンスコストの削減と、バグ修正コストの削減

今後の展望

E2E テスト自動化の分野は急速に進歩しており、AI との連携はさらに深化していくでしょう。本記事で紹介した手法をベースに、ビジュアルテスト、パフォーマンステスト、セキュリティテストなどの領域にも拡張していくことで、より包括的な品質保証システムを構築できます。

皆さまのプロジェクトにおいても、この全自動 E2E パイプラインを導入することで、より安心で効率的な開発体験を実現していただければと思います。継続的な改善により、ユーザーにとって価値のあるプロダクトを安定的に提供できる基盤を構築してください。

関連リンク