T-CREATOR

Playwright × Docker:本番環境に近い E2E テストを構築

Playwright × Docker:本番環境に近い E2E テストを構築

現代の Web アプリケーション開発において、品質保証は欠かせない要素となっています。特に End-to-End(E2E)テストは、実際のユーザー体験を再現し、アプリケーション全体の動作を検証する重要な手法です。

しかし、従来の E2E テスト手法では本番環境との差異により、テスト結果が不安定になったり、開発チーム間で実行環境が統一できないといった課題が存在しました。本記事では、Playwright と Docker を組み合わせることで、これらの課題を解決し、本番環境に近い安定した E2E テスト環境を構築する方法をご紹介いたします。

背景

Playwright とは

Playwright は Microsoft が開発したモダンな Web アプリケーション向け E2E テストフレームワークです。Chrome、Firefox、Safari(WebKit)など複数のブラウザエンジンに対応し、高速で信頼性の高いテスト実行を実現できます。

次の図は、Playwright の基本的な動作フローを示しています。

mermaidflowchart LR
    test[テストコード] -->|制御| browser[ブラウザエンジン]
    browser -->|操作| page[Webページ]
    page -->|結果| browser
    browser -->|レポート| test

    subgraph engines[対応ブラウザ]
        chrome[Chromium]
        firefox[Firefox]
        safari[WebKit]
    end

    browser -.->|切替可能| engines

Playwright の主な特徴は以下の通りです。

#説明
1複数ブラウザ対応(Chromium, Firefox, WebKit)
2高速な並列実行
3自動待機機能による安定性向上
4スクリーンショット・動画録画機能
5ネットワークインターセプト機能

Docker とは

Docker はコンテナ型仮想化技術を提供するプラットフォームで、アプリケーションとその実行環境を軽量なコンテナとしてパッケージ化できます。これにより、「どこでも同じ環境で動作する」を実現し、開発・テスト・本番環境の一致度を大幅に向上させることができます。

Docker の基本概念を図で示します。

mermaidflowchart TB
    dockerfile[Dockerfile]
    dockerfile -->|ビルド| image[Dockerイメージ]
    image -->|実行| container[コンテナ]

    subgraph host[ホストマシン]
        container
        container2[コンテナ2]
        container3[コンテナ3]
    end

    host -->|隔離された環境| isolation[環境の独立性]

Docker を使用することで得られるメリットは以下の通りです。

  • 環境の一貫性: 開発、テスト、本番環境で同じコンテナを使用
  • 依存関係の解決: 必要なライブラリやツールをコンテナに含める
  • スケーラビリティ: 必要に応じてコンテナの数を増減
  • 分離性: 他のプロセスに影響を与えない独立した環境

従来の E2E テストの課題

従来の E2E テストでは、以下のような課題が頻繁に発生していました。

環境依存による不安定性 開発者のローカル環境、CI/CD 環境、本番環境でブラウザのバージョンや OS、ライブラリのバージョンが異なることで、テスト結果にばらつきが生じます。

セットアップの複雑さ 各開発者が個別にブラウザドライバーやテスト環境をセットアップする必要があり、初期導入のハードルが高くなっていました。

リソース管理の困難さ 複数のテストを並列実行する際、システムリソースの競合や、テスト間でのデータ競合が発生しやすい状況でした。

課題

環境差異によるテスト結果の不整合

最も深刻な問題の一つが、実行環境の違いによってテスト結果が変わってしまうことです。

以下の図は、従来の環境差異による課題を示しています。

mermaidflowchart TD
    dev[開発環境] -->|テスト実行| result1[✓ 成功]
    ci[CI/CD環境] -->|同じテスト実行| result2[✗ 失敗]
    prod[本番環境] -->|実際の動作| result3[? 不明]

    subgraph issues[差異の原因]
        browser_ver[ブラウザバージョン]
        os_diff[OS の違い]
        network[ネットワーク設定]
        timezone[タイムゾーン]
    end

    result2 -.->|原因| issues

具体的な課題例:

  • ブラウザバージョンの違い: 開発者の Chrome と CI 環境の Chromium で動作が異なる
  • 画面解像度の差異: スクリーンショット比較テストで予期しない差分が発生
  • タイムゾーンの設定: 日時処理に関するテストで意図しない結果

CI/CD 環境でのテスト実行の困難さ

継続的インテグレーション・継続的デリバリー(CI/CD)パイプラインでの E2E テスト実行には、特有の課題があります。

実行時間の長期化 E2E テストは単体テストと比較して実行時間が長く、CI/CD パイプラインのボトルネックになりがちです。

デバッグの困難さ CI 環境でテストが失敗した場合、ローカル環境では再現できないケースが多く、原因の特定に時間がかかります。

リソースの制約 CI/CD サーバーのメモリや CPU リソースが限られているため、複数のブラウザを同時に起動することが困難な場合があります。

スケーラビリティの問題

プロジェクトの成長とともに、E2E テストの数も増加します。しかし、従来の手法では以下の問題が発生します:

mermaidgraph LR
    small[小規模プロジェクト] -->|テスト増加| medium[中規模プロジェクト]
    medium -->|さらに増加| large[大規模プロジェクト]

    small -->|実行時間| time1[5分]
    medium -->|実行時間| time2[30分]
    large -->|実行時間| time3[2時間+]

    time3 -->|問題| issues2[開発サイクルの悪化]

並列実行の制限 単一のマシンでは同時に実行できるブラウザインスタンス数に限界があります。

メンテナンス性の悪化 テスト環境の構築・維持が複雑になり、開発チーム全体の生産性に影響を与えます。

解決策

Playwright × Docker アーキテクチャ

Playwright と Docker を組み合わせることで、前述の課題を効果的に解決できます。

以下の図は、Playwright × Docker アーキテクチャの全体像を示しています。

mermaidflowchart TB
    subgraph docker_env[Docker環境]
        subgraph container1[Playwrightコンテナ1]
            pw1[Playwright]
            browser1[ブラウザ群]
        end

        subgraph container2[Playwrightコンテナ2]
            pw2[Playwright]
            browser2[ブラウザ群]
        end

        subgraph app_container[アプリケーションコンテナ]
            webapp[Webアプリ]
            database[(DB)]
        end
    end

    test_code[テストコード] -->|実行指示| container1
    test_code -->|実行指示| container2

    container1 -->|HTTP通信| app_container
    container2 -->|HTTP通信| app_container

    container1 -->|結果| results[テスト結果]
    container2 -->|結果| results

このアーキテクチャでは、以下の要素が連携します:

#役割
1Playwright コンテナ: テスト実行環境を提供
2アプリケーションコンテナ: テスト対象の Web アプリケーション
3データベースコンテナ: テストデータの管理
4Docker Compose: 複数コンテナの統合管理

コンテナ化によるメリット

Docker コンテナ化により、以下のメリットを享受できます。

環境の完全な統一 開発者のローカル環境、CI/CD 環境、本番環境で同じ Docker イメージを使用することで、環境差異を根本的に解決します。

依存関係の確実な管理 必要なブラウザ、ドライバー、ライブラリをすべてコンテナに含めることで、「動かない」問題を回避できます。

スケーラブルな並列実行 複数のコンテナを起動することで、テストの並列実行数を柔軟に調整可能です。

mermaidsequenceDiagram
    participant Dev as 開発者
    participant Docker as Docker
    participant Container as コンテナ群
    participant App as テスト対象アプリ

    Dev->>Docker: docker-compose up
    Docker->>Container: コンテナ群起動
    Container->>App: 並列テスト実行
    App->>Container: テスト結果
    Container->>Docker: 結果集約
    Docker->>Dev: 統合レポート

本番環境との一致度向上

Docker を使用することで、本番環境とテスト環境の一致度を飛躍的に向上させることができます。

インフラストラクチャ as Code Dockerfile と Docker Compose ファイルによって、インフラストラクチャをコードとして管理し、バージョン管理システムで追跡できます。

本番データの安全な活用 本番環境のデータベースダンプを匿名化してテスト用コンテナで使用することで、より現実的なテストが可能になります。

ネットワーク環境の再現 Docker Network を使用して、本番環境のマイクロサービス構成を忠実に再現できます。

具体例

Docker 環境のセットアップ

まずは基本的な Docker 環境を構築します。プロジェクトのルートディレクトリに以下のファイルを作成しましょう。

package.json の準備 必要なパッケージを定義します。

json{
  "name": "playwright-docker-e2e",
  "version": "1.0.0",
  "scripts": {
    "test:e2e": "playwright test",
    "test:e2e:docker": "docker-compose up --abort-on-container-exit"
  },
  "devDependencies": {
    "@playwright/test": "^1.40.0"
  }
}

Dockerfile の作成 Playwright が動作するコンテナ環境を定義します。

dockerfileFROM mcr.microsoft.com/playwright:v1.40.0-focal

# 作業ディレクトリの設定
WORKDIR /app

# package.jsonとyarn.lockをコピー
COPY package.json yarn.lock ./

# 依存関係のインストール
RUN yarn install --frozen-lockfile

依存関係のインストール後、アプリケーションのソースコードをコピーします。

dockerfile# アプリケーションファイルのコピー
COPY . .

# Playwrightブラウザの追加インストール(必要な場合)
RUN npx playwright install

# テスト実行のデフォルトコマンド
CMD ["yarn", "test:e2e"]

docker-compose.yml の設定 複数のサービスを連携させるための設定を行います。

yamlversion: '3.8'

services:
  # テスト対象のWebアプリケーション
  web-app:
    build:
      context: ./app
      dockerfile: Dockerfile
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=test
    depends_on:
      - database

データベースサービスとテスト実行サービスを追加します。

yaml  # テスト用データベース
  database:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: testdb
      POSTGRES_USER: testuser
      POSTGRES_PASSWORD: testpass
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  # Playwrightテスト実行環境
  e2e-tests:
    build:
      context: .
      dockerfile: Dockerfile.playwright
    environment:
      - BASE_URL=http://web-app:3000
    depends_on:
      - web-app
    volumes:
      - ./test-results:/app/test-results
      - ./playwright-report:/app/playwright-report

volumes:
  postgres_data:

Playwright の設定とカスタマイズ

次に、Playwright の設定を Docker 環境に最適化します。

playwright.config.js の基本設定 テスト実行の基本設定を行います。

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

export default defineConfig({
  // テストディレクトリの指定
  testDir: './tests',

  // 並列実行の設定
  fullyParallel: true,

  // CI環境での失敗時リトライ設定
  retries: process.env.CI ? 2 : 0,

  // テストタイムアウト設定
  timeout: 30000,
});

ブラウザとプロジェクト設定を追加します。

javascriptexport default defineConfig({
  // ... 前の設定

  // テスト結果の出力設定
  reporter: [
    ['html'],
    ['json', { outputFile: 'test-results/results.json' }],
    ['junit', { outputFile: 'test-results/junit.xml' }],
  ],

  use: {
    // ベースURL(Docker環境用)
    baseURL:
      process.env.BASE_URL || 'http://localhost:3000',

    // スクリーンショット設定
    screenshot: 'only-on-failure',

    // 動画録画設定
    video: 'retain-on-failure',

    // ブラウザコンテキストの設定
    viewport: { width: 1280, height: 720 },
  },

  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
  ],
});

環境変数による設定の動的変更 Docker 環境とローカル環境で設定を切り替えられるようにします。

javascript// Docker環境の検出
const isDocker = process.env.DOCKER_ENV === 'true';

export default defineConfig({
  // Docker環境では並列実行数を制限
  workers: isDocker ? 2 : undefined,

  use: {
    baseURL: isDocker
      ? 'http://web-app:3000'
      : 'http://localhost:3000',

    // Docker環境では headless モードを強制
    headless: isDocker || process.env.CI === 'true',
  },
});

CI/CD パイプラインへの組み込み

GitHub Actions を使用した CI/CD パイプラインの設定例をご紹介します。

GitHub Actions ワークフローファイル .github​/​workflows​/​e2e.ymlを作成します。

yamlname: E2E Tests

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

jobs:
  e2e-tests:
    runs-on: ubuntu-latest

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

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

Docker 環境でのテスト実行を設定します。

yaml- name: Run E2E tests with Docker
  run: |
    docker-compose up --build --abort-on-container-exit

- name: Upload test results
  uses: actions/upload-artifact@v3
  if: always()
  with:
    name: playwright-report
    path: |
      test-results/
      playwright-report/
    retention-days: 7

並列実行の最適化 CI 環境でのリソースを有効活用するため、テストを分割実行します。

yamljobs:
  e2e-tests:
    strategy:
      matrix:
        shard: [1/3, 2/3, 3/3]

    steps:
      # ... 前のステップ

      - name: Run E2E tests (Shard ${{ matrix.shard }})
        run: |
          export PLAYWRIGHT_SHARD=${{ matrix.shard }}
          docker-compose up --build --abort-on-container-exit

実際のテストケース実装

実際の Playwright テストコードを作成します。

基本的なテストケース ユーザーログインフローのテストを実装します。

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

test.describe('User Authentication', () => {
  test('should login with valid credentials', async ({
    page,
  }) => {
    // ログインページへの遷移
    await page.goto('/login');

    // フォーム入力
    await page.fill(
      'input[name="email"]',
      'test@example.com'
    );
    await page.fill(
      'input[name="password"]',
      'password123'
    );

    // ログインボタンクリック
    await page.click('button[type="submit"]');

    // ダッシュボードへのリダイレクトを確認
    await expect(page).toHaveURL('/dashboard');
    await expect(page.locator('h1')).toContainText(
      'Welcome'
    );
  });
});

データ駆動テスト 複数のテストデータを使用したテストケースです。

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

const testUsers = [
  {
    email: 'admin@example.com',
    password: 'admin123',
    role: 'admin',
  },
  {
    email: 'user@example.com',
    password: 'user123',
    role: 'user',
  },
  {
    email: 'guest@example.com',
    password: 'guest123',
    role: 'guest',
  },
];

testUsers.forEach(({ email, password, role }) => {
  test(`should login as ${role}`, async ({ page }) => {
    await page.goto('/login');
    await page.fill('input[name="email"]', email);
    await page.fill('input[name="password"]', password);
    await page.click('button[type="submit"]');

    // ロールに応じた画面表示の確認
    const expectedText =
      role === 'admin' ? 'Admin Panel' : 'Dashboard';
    await expect(page.locator('h1')).toContainText(
      expectedText
    );
  });
});

API 通信のモック 外部 API との連携をテストする場合のモック設定です。

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

test('should handle API errors gracefully', async ({
  page,
}) => {
  // APIレスポンスをモック
  await page.route('**/api/users/**', (route) => {
    route.fulfill({
      status: 500,
      contentType: 'application/json',
      body: JSON.stringify({
        error: 'Internal Server Error',
      }),
    });
  });

  await page.goto('/users');

  // エラーメッセージの表示確認
  await expect(
    page.locator('.error-message')
  ).toBeVisible();
  await expect(
    page.locator('.error-message')
  ).toContainText('データの取得に失敗');
});

スクリーンショット比較テスト 視覚的回帰テストの実装例です。

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

test('visual regression test for dashboard', async ({
  page,
}) => {
  await page.goto('/dashboard');

  // 動的コンテンツの読み込み待機
  await page.waitForSelector('.dashboard-content');

  // 時刻表示などの動的要素をマスク
  await page.locator('.current-time').hover();

  // スクリーンショット比較
  await expect(page).toHaveScreenshot('dashboard.png', {
    mask: [page.locator('.current-time')],
    fullPage: true,
  });
});

これらの具体例により、Docker 環境での Playwright E2E テストの実装方法が明確になります。各段階で適切な設定と実装を行うことで、安定性と保守性を兼ね備えたテスト環境を構築できるでしょう。

まとめ

本記事では、Playwright と Docker を組み合わせた本番環境に近い E2E テスト環境の構築方法について詳しく解説いたしました。

従来の E2E テストで直面していた環境差異による不整合、CI/CD 環境での実行困難さ、スケーラビリティの問題を、コンテナ化技術によって根本的に解決できることをご理解いただけたかと思います。

導入による主要なメリット

  • 環境統一: 開発・テスト・本番環境での一貫した動作
  • 安定性向上: 環境依存による不安定なテスト結果の解消
  • 開発効率化: セットアップの簡素化と並列実行による高速化
  • 保守性向上: インフラストラクチャのコード化による管理の簡素化

今後の展開

本記事で紹介した基本的な構成をベースに、以下のような発展的な取り組みも可能です。

  • Kubernetes 環境での大規模並列実行
  • テストデータ管理の自動化
  • パフォーマンステストとの統合
  • セキュリティテストの組み込み

Playwright と Docker の組み合わせは、現代の Web アプリケーション開発において、品質保証の新しいスタンダードとなることでしょう。皆様のプロジェクトでも、ぜひこの手法を取り入れて、より信頼性の高いアプリケーション開発を実現してください。

関連リンク