mcpField
P
playwright-testing

playwright-testing

github.com/fcakyonテスト・品質1.2k39 回インストール1 日前に更新
リポジトリ

playwright-testing について

Playwrightでテストを書く開発者向けに、テスト構成、ロケーター、認証、ファイル操作、ネットワークモック、デバッグの実践方法をまとめています。不安定なテストを減らす方法に加え、responsive-testerによるレスポンシブレイアウトの確認も紹介します。

主な機能

  1. 01機能ごとのテストファイルと命名規則
  2. 02セマンティックロケーターとPage Object
  3. 03認証状態の再利用と複数ユーザーのテスト
  4. 04ファイルのアップロードとダウンロード
  5. 05ネットワークモックと失敗時のデバッグ
  6. 06レスポンシブなブレークポイントとレイアウトの確認

活用シーン

  1. 01ログインやダッシュボードのE2Eテスト構成
  2. 02異なる権限のユーザーに対する認証状態の再利用
  3. 03APIレスポンスをモックした画面動作の検証
  4. 04競合状態や不安定なテストの調査
  5. 05モバイルとデスクトップの画面レイアウト確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/fcakyon/claude-codex-settings --skill playwright-testing

インストール後、Claude Code で/playwright-testingを入力して読み込まれたか確認します。

詳細

Playwright のテストのベストプラクティス

テストの構成

ファイル構成
tests/
├── auth/
│   ├── login.spec.ts
│   └── signup.spec.ts
├── dashboard/
│   └── dashboard.spec.ts
├── fixtures/
│   └── test-data.ts
├── pages/
│   └── login.page.ts
└── playwright.config.ts
命名規則
  • ファイル:feature-name.spec.ts
  • テスト:実装ではなく、ユーザーの動作を記述します
  • 良い例:test('user can reset password via email')
  • 悪い例:test('test reset password')

ページオブジェクトモデル

基本パターン
// 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("Email").fill(email);
    await this.page.getByLabel("Password").fill(password);
    await this.page.getByRole("button", { name: "Sign in" }).click();
  }
}

// tests/login.spec.ts
test("successful login", async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login("[email protected]", "password");
  await expect(page).toHaveURL("/dashboard");
});

ロケーター戦略

優先順位(高い順)
  1. getByRole - アクセシビリティに対応し、堅牢です
  2. getByLabel - フォーム入力向けです
  3. getByPlaceholder - ラベルがない場合に使用します
  4. getByText - 表示テキスト向けです
  5. getByTestId - より適切な方法がない場合に使用します
  6. CSS/XPath - 最終手段です
例
// Preferred
await page.getByRole("button", { name: "Submit" }).click();
await page.getByLabel("Email address").fill("[email protected]");

// Acceptable
await page.getByTestId("submit-button").click();

// Avoid
await page.locator("#submit-btn").click();
await page.locator('//button[@type="submit"]').click();

認証の処理

ストレージ状態(推奨)

ログイン状態を保存し、テスト間で再利用します。

// global-setup.ts
async function globalSetup() {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto("/login");
  await page.getByLabel("Email").fill(process.env.TEST_USER_EMAIL);
  await page.getByLabel("Password").fill(process.env.TEST_USER_PASSWORD);
  await page.getByRole("button", { name: "Sign in" }).click();
  await page.waitForURL("/dashboard");
  await page.context().storageState({ path: "auth.json" });
  await browser.close();
}

// playwright.config.ts
export default defineConfig({
  globalSetup: "./global-setup.ts",
  use: {
    storageState: "auth.json",
  },
});
複数ユーザーのシナリオ
// Create different auth states
const adminAuth = "admin-auth.json";
const userAuth = "user-auth.json";

test.describe("admin features", () => {
  test.use({ storageState: adminAuth });
  // Admin tests
});

test.describe("user features", () => {
  test.use({ storageState: userAuth });
  // User tests
});

ファイルアップロードの処理

基本的なアップロード
// Single file
await page.getByLabel("Upload file").setInputFiles("path/to/file.pdf");

// Multiple files
await page
  .getByLabel("Upload files")
  .setInputFiles(["path/to/file1.pdf", "path/to/file2.pdf"]);

// Clear file input
await page.getByLabel("Upload file").setInputFiles([]);
ドラッグ&ドロップによるアップロード
// Create file from buffer
const buffer = Buffer.from("file content");

await page.getByTestId("dropzone").dispatchEvent("drop", {
  dataTransfer: {
    files: [{ name: "test.txt", mimeType: "text/plain", buffer }],
  },
});
ファイルのダウンロード
const downloadPromise = page.waitForEvent("download");
await page.getByRole("button", { name: "Download" }).click();
const download = await downloadPromise;
await download.saveAs("downloads/" + download.suggestedFilename());

待機戦略

自動待機(推奨)

Playwright は要素を自動で待機します。アサーションを使用します。

// Auto-waits for element to be visible and stable
await page.getByRole("button", { name: "Submit" }).click();

// Auto-waits for condition
await expect(page.getByText("Success")).toBeVisible();
明示的な待機(必要な場合)
// Wait for navigation
await page.waitForURL("**/dashboard");

// Wait for network idle
await page.waitForLoadState("networkidle");

// Wait for specific response
await page.waitForResponse((resp) => resp.url().includes("/api/data"));

ネットワークのモック

API レスポンスのモック
await page.route("**/api/users", async (route) => {
  await route.fulfill({
    status: 200,
    contentType: "application/json",
    body: JSON.stringify([{ id: 1, name: "Test User" }]),
  });
});

// Mock error response
await page.route("**/api/users", async (route) => {
  await route.fulfill({ status: 500 });
});
インターセプトと変更
await page.route("**/api/data", async (route) => {
  const response = await route.fetch();
  const json = await response.json();
  json.modified = true;
  await route.fulfill({ response, json });
});

CI/CD との統合

GitHub Actions の例
- name: Run Playwright tests
  run: npx playwright test
  env:
    CI: true

- name: Upload test results
  if: always()
  uses: actions/upload-artifact@v3
  with:
    name: playwright-report
    path: playwright-report/
並列実行
// playwright.config.ts
export default defineConfig({
  workers: process.env.CI ? 2 : undefined,
  fullyParallel: true,
});

失敗したテストのデバッグ

デバッグツール
# Run with UI mode
npx playwright test --ui

# Run with inspector
npx playwright test --debug

# Show browser
npx playwright test --headed
トレースビューアー
// playwright.config.ts
use: {
  trace: 'on-first-retry', // Capture trace on failure
}

不安定なテストの修正

よくある原因と解決策

競合状態:

  • 長時間の固定待機ではなく、適切なアサーションを使用します
  • ネットワークリクエストの完了を待ちます

アニメーションの問題:

  • テスト設定でアニメーションを無効にします
  • アニメーションの完了を待ちます

動的コンテンツ:

  • 柔軟なロケーター(位置ではなくテキストコンテンツ)を使用します
  • 読み込み状態が解消されるまで待ちます

テストの分離:

  • 各テストで独自の状態をセットアップします
  • 他のテストの副作用に依存しないようにします
避けるべきアンチパターン
// Bad: Hard sleep
await page.waitForTimeout(5000);

// Good: Wait for condition
await expect(page.getByText("Loaded")).toBeVisible();

// Bad: Flaky selector
await page.locator(".btn:nth-child(3)").click();

// Good: Semantic selector
await page.getByRole("button", { name: "Submit" }).click();

レスポンシブデザインのテスト

ビューポートのブレークポイント全体で包括的なレスポンシブテストを行うには、responsive-tester エージェントを使用します。次の項目を自動で実行します。

  • 7 つの標準ブレークポイント(375px から 1536px)でページをテストします
  • 横方向のオーバーフローの問題を検出します
  • モバイルファーストのデザインパターンを検証します
  • タッチターゲットのサイズ(最小 44x44px)を確認します
  • モバイル向けの代替表示がない固定幅などのアンチパターンを検出します

「レスポンシブをテストして」「ブレークポイントを確認して」「モバイル/デスクトップのレイアウトをテストして」と依頼すると起動します。

コメント