playwright-testing について
Playwrightでテストを書く開発者向けに、テスト構成、ロケーター、認証、ファイル操作、ネットワークモック、デバッグの実践方法をまとめています。不安定なテストを減らす方法に加え、responsive-testerによるレスポンシブレイアウトの確認も紹介します。
主な機能
- 01機能ごとのテストファイルと命名規則
- 02セマンティックロケーターとPage Object
- 03認証状態の再利用と複数ユーザーのテスト
- 04ファイルのアップロードとダウンロード
- 05ネットワークモックと失敗時のデバッグ
- 06レスポンシブなブレークポイントとレイアウトの確認
活用シーン
- 01ログインやダッシュボードのE2Eテスト構成
- 02異なる権限のユーザーに対する認証状態の再利用
- 03APIレスポンスをモックした画面動作の検証
- 04競合状態や不安定なテストの調査
- 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");
});
ロケーター戦略
優先順位(高い順)
getByRole- アクセシビリティに対応し、堅牢ですgetByLabel- フォーム入力向けですgetByPlaceholder- ラベルがない場合に使用しますgetByText- 表示テキスト向けですgetByTestId- より適切な方法がない場合に使用します- 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)を確認します
- モバイル向けの代替表示がない固定幅などのアンチパターンを検出します
「レスポンシブをテストして」「ブレークポイントを確認して」「モバイル/デスクトップのレイアウトをテストして」と依頼すると起動します。
コメント
Markdown 対応