Advanced

Playwright E2E with Claude Code

Aprenda sobre playwright e2e usando o Claude Code. Dicas praticas e exemplos de codigo incluidos.

PlaywrightでE2Eテストを自動化する

Playwrightはクロスブラウザ対応のE2Eテストフレームワークです。Chromium、Firefox、WebKitの3つのブラウザエンジンをサポートし、安定したテスト実行を実現します。Claude Codeはテストシナリオの設計からページオブジェクトの構築まで的確にサポートします。

ページオブジェクトパターン

Claude Codeにページオブジェクトの設計を依頼しましょう。

> Playwrightでログインページのページオブジェクトを作って。
> エラー表示の確認、成功時のリダイレクト確認を含めて。
import { Page, Locator, expect } from "@playwright/test";

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;
  readonly errorMessage: Locator;
  readonly rememberMeCheckbox: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.getByLabel("メールアドレス");
    this.passwordInput = page.getByLabel("パスワード");
    this.submitButton = page.getByRole("button", { name: "ログイン" });
    this.errorMessage = page.getByRole("alert");
    this.rememberMeCheckbox = page.getByLabel("ログイン状態を保持");
  }

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

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }

  async expectError(message: string) {
    await expect(this.errorMessage).toContainText(message);
  }

  async expectRedirectTo(path: string) {
    await expect(this.page).toHaveURL(new RegExp(path));
  }
}

テストシナリオの実装

ページオブジェクトを使ったテストの記述です。

import { test, expect } from "@playwright/test";
import { LoginPage } from "./pages/login-page";
import { DashboardPage } from "./pages/dashboard-page";

test.describe("認証フロー", () => {
  let loginPage: LoginPage;

  test.beforeEach(async ({ page }) => {
    loginPage = new LoginPage(page);
    await loginPage.goto();
  });

  test("有効な認証情報でログインできる", async ({ page }) => {
    await loginPage.login("[email protected]", "password123");
    await loginPage.expectRedirectTo("/dashboard");

    const dashboard = new DashboardPage(page);
    await expect(dashboard.welcomeMessage).toContainText("ようこそ");
  });

  test("無効なパスワードでエラーが表示される", async () => {
    await loginPage.login("[email protected]", "wrong-password");
    await loginPage.expectError("メールアドレスまたはパスワードが正しくありません");
  });

  test("空のフォームでバリデーションエラーが表示される", async () => {
    await loginPage.submitButton.click();
    await expect(loginPage.emailInput).toHaveAttribute("aria-invalid", "true");
  });
});

認証状態の共有

テスト間で認証状態を共有して実行速度を向上させるパターンです。

// auth.setup.ts
import { test as setup, expect } from "@playwright/test";

const authFile = "playwright/.auth/user.json";

setup("authentication", async ({ page }) => {
  await page.goto("/login");
  await page.getByLabel("メールアドレス").fill("[email protected]");
  await page.getByLabel("パスワード").fill("password123");
  await page.getByRole("button", { name: "ログイン" }).click();

  await page.waitForURL("/dashboard");
  await expect(page.getByText("ようこそ")).toBeVisible();

  await page.context().storageState({ path: authFile });
});

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

export default defineConfig({
  projects: [
    { name: "setup", testMatch: /.*\.setup\.ts/ },
    {
      name: "chromium",
      use: {
        storageState: "playwright/.auth/user.json",
      },
      dependencies: ["setup"],
    },
  ],
});

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

テスト中のAPIリクエストをインターセプトするパターンです。

test("APIエラー時にエラー画面が表示される", async ({ page }) => {
  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.getByText("データの取得に失敗しました")).toBeVisible();
  await expect(page.getByRole("button", { name: "再試行" })).toBeVisible();
});

test("遅いネットワークでローディング表示される", async ({ page }) => {
  await page.route("**/api/users", async (route) => {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    await route.continue();
  });

  await page.goto("/users");
  await expect(page.getByText("読み込み中")).toBeVisible();
});

ビジュアルリグレッションテスト

スクリーンショット比較で視覚的な変更を検出します。

test("ダッシュボード画面のビジュアルチェック", async ({ page }) => {
  await page.goto("/dashboard");
  await page.waitForLoadState("networkidle");

  await expect(page).toHaveScreenshot("dashboard.png", {
    maxDiffPixelRatio: 0.01,
    animations: "disabled",
  });
});

test("モバイル表示のビジュアルチェック", async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 812 });
  await page.goto("/dashboard");

  await expect(page).toHaveScreenshot("dashboard-mobile.png");
});

CI/CDでの設定

GitHub Actionsでの実行設定です。

# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]

jobs:
  e2e:
    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: failure()
        with:
          name: playwright-report
          path: playwright-report/

Summary

Playwrightはクロスブラウザ対応の安定したE2Eテスト環境を提供します。Claude Codeを活用すれば、ページオブジェクト設計、認証フロー、ビジュアルテストなどの複雑なテストシナリオも効率的に構築可能です。

ユニットテストの高度な手法はVitest上級テクニックを、APIモックの詳細はMSW APIモック活用ガイドを参照してください。Playwright公式ドキュメントも確認しておきましょう。

#Claude Code #Playwright #E2Eテスト #自動テスト #quality assurance