Advanced

Playwright E2E: Claude Code 활용 가이드

playwright e2e: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.

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/

정리

Playwrightはクロス브라우저대응の安定したE2E 테스트環境を提供します。Claude Codeを활용すれば、페이지객체설계、인증フロー、ビジュアル테스트などの複雑な테스트シナリオも효율적으로구축가능합니다。

단위 테스트の高度な手法はVitest上級テクニックを、API모크의 상세 정보는MSW API모크활용가이드를 참고하세요.Playwright공식 문서도 확인해 두세요.

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