メインコンテンツへスキップ
Code & Craft
ツール比較 約6分で読めます

Playwright E2Eテスト実践ガイド|セットアップから CI 連携まで【2026年版】

Playwright による E2E テストの実装方法を完全解説。セットアップ、要素の待機、スクリーンショット、並列実行、GitHub Actions連携まで実例付きで紹介。

Playwright は Microsoft が開発する E2E(End-to-End)テストフレームワークで、2026年現在では最も人気のあるテストツールの一つです。Chromium、Firefox、WebKit の全主要ブラウザを単一のAPIで操作でき、TypeScript・Python・.NET・Java に対応しています。

Playwright の特徴

  • クロスブラウザ: Chrome、Firefox、Safari(WebKit)を単一コードで
  • 高速: 並列実行で大量のテストを短時間で
  • デバッグしやすい: トレース、ビデオ、スクリーンショット自動記録
  • 自動待機: 要素の出現を自動的に待つ
  • ネットワーク制御: API モック、レスポンス改ざん
  • モバイルエミュレーション: iPhone、Android デバイスの模擬

Cypress との比較

項目CypressPlaywright
対応ブラウザChromium系 + Firefox全主要ブラウザ(Safari も)
実行速度普通高速
並列実行有料無料
複数タブ制限あり対応
iframe制限あり対応
価格一部有料完全無料
API独自シンプル

インストール

npm init playwright@latest

対話的にセットアップされます。以下を選択:

  • TypeScript / JavaScript
  • テストディレクトリ(tests 推奨)
  • GitHub Actions の追加(推奨)
  • Playwright ブラウザのインストール
# ブラウザを個別にインストール
npx playwright install

基本的なテスト

// tests/example.spec.ts
import { test, expect } from '@playwright/test';

test('ホームページのタイトルが正しい', async ({ page }) => {
  await page.goto('https://clvr.lol');
  await expect(page).toHaveTitle(/Code & Craft/);
});

test('記事ページに遷移できる', async ({ page }) => {
  await page.goto('https://clvr.lol');
  await page.getByRole('link', { name: '記事一覧' }).click();
  await expect(page).toHaveURL(/\/blog/);
});

要素の取得方法

Playwright は ロール(Role)ベースのセレクタを推奨しています。

推奨: セマンティックなロケーター

// Role ベース(最優先)
await page.getByRole('button', { name: '送信' }).click();
await page.getByRole('link', { name: 'ホーム' }).click();
await page.getByRole('heading', { name: 'タイトル' });

// Label ベース
await page.getByLabel('メールアドレス').fill('test@example.com');

// Placeholder
await page.getByPlaceholder('パスワード').fill('secret');

// テキスト
await page.getByText('こんにちは').click();

// Test ID(カスタム属性 data-testid)
await page.getByTestId('submit-button').click();

非推奨: CSS セレクタ

// 避ける(HTML 構造に依存)
await page.locator('#btn-submit').click();
await page.locator('.header > nav a').click();

アサーション

// 要素の状態
await expect(page.getByRole('button')).toBeVisible();
await expect(page.getByRole('button')).toBeEnabled();
await expect(page.getByRole('button')).toBeDisabled();
await expect(page.getByRole('checkbox')).toBeChecked();

// テキスト
await expect(page.getByRole('heading')).toHaveText('タイトル');
await expect(page.getByRole('heading')).toContainText('タイトル');

// URL
await expect(page).toHaveURL('/blog');
await expect(page).toHaveURL(/\/blog\/\d+/);

// ページタイトル
await expect(page).toHaveTitle(/Code & Craft/);

// 要素数
await expect(page.getByRole('article')).toHaveCount(10);

// 属性
await expect(page.getByRole('link')).toHaveAttribute('href', '/about');

// スクリーンショット比較
await expect(page).toHaveScreenshot('homepage.png');

Playwright のアサーションは 自動的にリトライします。要素が出現するまで最大 5 秒(デフォルト)待機してくれるため、手動の waitFor は不要です。

フォーム操作

test('ログインフォーム', async ({ page }) => {
  await page.goto('/login');

  // 入力
  await page.getByLabel('メールアドレス').fill('user@example.com');
  await page.getByLabel('パスワード').fill('password');

  // チェックボックス
  await page.getByLabel('ログイン状態を保持').check();

  // セレクトボックス
  await page.getByLabel('言語').selectOption('ja');

  // ファイルアップロード
  await page.getByLabel('アバター').setInputFiles('./test-files/avatar.png');

  // 送信
  await page.getByRole('button', { name: 'ログイン' }).click();

  // 遷移確認
  await expect(page).toHaveURL('/dashboard');
});

待機(待ち処理)

Playwright は自動待機が基本ですが、明示的な待機も可能です。

// 要素が表示されるまで待機
await page.waitForSelector('.content');

// 特定のURLに遷移するまで待機
await page.waitForURL('/dashboard');

// ネットワークが落ち着くまで待機
await page.waitForLoadState('networkidle');

// カスタム条件で待機
await page.waitForFunction(() => document.title.includes('完了'));

// 時間で待機(非推奨)
await page.waitForTimeout(1000);

ネットワーク制御とモック

API レスポンスのモック

test('APIエラー時の表示', async ({ page }) => {
  await page.route('**/api/users', (route) => {
    route.fulfill({
      status: 500,
      contentType: 'application/json',
      body: JSON.stringify({ error: 'サーバーエラー' }),
    });
  });

  await page.goto('/users');
  await expect(page.getByText('エラーが発生しました')).toBeVisible();
});

レスポンスの監視

test('API レスポンスを検証', async ({ page }) => {
  const responsePromise = page.waitForResponse('**/api/posts');
  await page.goto('/blog');
  const response = await responsePromise;

  expect(response.status()).toBe(200);
  const data = await response.json();
  expect(data.length).toBeGreaterThan(0);
});

スクリーンショット・ビデオ

// フルページスクリーンショット
await page.screenshot({ path: 'fullpage.png', fullPage: true });

// 特定要素のスクリーンショット
await page.getByRole('article').screenshot({ path: 'article.png' });

// ビジュアルリグレッションテスト
await expect(page).toHaveScreenshot('homepage.png', {
  maxDiffPixels: 100,
});

設定ファイル playwright.config.ts でトレース・ビデオの自動記録を有効化:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    trace: 'on-first-retry',       // 失敗時のみトレース
    screenshot: 'only-on-failure', // 失敗時のみスクショ
    video: 'retain-on-failure',    // 失敗時のみ保存
  },
});

並列実行と複数ブラウザ

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

export default defineConfig({
  fullyParallel: true,
  workers: process.env.CI ? 2 : 4,

  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
    },
    {
      name: 'Mobile Safari',
      use: { ...devices['iPhone 13'] },
    },
  ],
});
# 全ブラウザでテスト
npx playwright test

# 特定ブラウザのみ
npx playwright test --project=chromium

# 並列数を指定
npx playwright test --workers=8

デバッグツール

Inspector

npx playwright test --debug

コードステップ実行とライブ検査ができます。

Trace Viewer

失敗時のトレースを詳細に確認:

npx playwright show-trace trace.zip

各ステップのスナップショット、コンソール、ネットワークを閲覧できます。

Codegen(コード自動生成)

npx playwright codegen https://clvr.lol

ブラウザ操作をそのままテストコードに変換してくれます。テスト初心者に特に便利です。

Page Object Model

複雑なテストは Page Object パターンで整理します。

// pages/login.page.ts
export class LoginPage {
  constructor(private page: Page) {}

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.page.getByLabel('メールアドレス').fill(email);
    await this.page.getByLabel('パスワード').fill(password);
    await this.page.getByRole('button', { name: 'ログイン' }).click();
  }
}
// tests/login.spec.ts
import { test } from '@playwright/test';
import { LoginPage } from '../pages/login.page';

test('ログイン成功', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('user@example.com', 'password');
});

GitHub Actions での実行

name: Playwright Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

まとめ

Playwright は、2026年現在における E2E テストの最有力選択肢です。

導入を推奨するケース:

  • 新規プロジェクト
  • Cypress の機能制限(クロスブラウザ、並列実行)に困っている
  • モバイル対応のテストが必要
  • ビジュアルリグレッションテストをしたい

学習ステップ:

  1. codegen でテストを自動生成して試す
  2. 基本的なアサーションとロケーターを覚える
  3. Page Object パターンに進化させる
  4. CI/CD に統合

始めるのは簡単で、スケールも容易です。まずは 3-5 個のクリティカルパスをカバーするテストから始めましょう。

参考リンク

#Playwright #E2E テスト #テスト #CI/CD #自動化
シェア: