e2e-testing
Python/ReactフルスタックアプリケーションにおけるPlaywrightを使ったE2Eテストのパターンを提供します。ログイン・CRUD・ナビゲーションなどの完全なユーザーワークフロー、クリティカルパスの回帰テスト、クロスブラウザ検証を記述する際に使用してください。テスト構造・ページオブジェクトモデル・セレクター戦略(data-testid → role → label の優先順)・待機戦略・認証状態の再利用・テストデータ管理・CI連携をカバーしますが、ユニットテストやコンポーネントテストは対象外です。
description の原文を見る
>- End-to-end testing patterns with Playwright for full-stack Python/React applications. Use when writing E2E tests for complete user workflows (login, CRUD, navigation), critical path regression tests, or cross-browser validation. Covers test structure, page object model, selector strategy (data-testid > role > label), wait strategies, auth state reuse, test data management, and CI integration. Does NOT cover unit tests or component tests (use pytest-patterns or react-testing-patterns).
SKILL.md 本文
E2E テスト
使用するタイミング
以下の場合にこのスキルを有効化してください:
- ログイン、CRUD操作、複数ページにまたがるフローなど、完全なユーザーワークフローのE2Eテストを書く
- フルスタック全体を検証するクリティカルパス回帰テストを作成する
- ブラウザ互換性テスト(Chromium、Firefox、WebKit)を行う
- 認証フロー全体のE2Eテストを検証する
- ファイルのアップロード/ダウンロードワークフローをテストする
- デプロイメント検証用のスモークテストを書く
このスキルを使用しないでください:
- React コンポーネント単体テスト (
react-testing-patternsを使用) - Python バックエンド単体/統合テスト (
pytest-patternsを使用) - TDD ワークフロー強制 (
tdd-workflowを使用) - ブラウザなしの API コントラクトテスト (
pytest-patternsと httpx を使用)
手順
テスト構造
e2e/
├── playwright.config.ts # Playwright グローバル設定
├── fixtures/
│ ├── auth.fixture.ts # 認証状態のセットアップ
│ └── test-data.fixture.ts # テストデータの作成/クリーンアップ
├── pages/
│ ├── base.page.ts # 共有メソッドを持つベースページオブジェクト
│ ├── login.page.ts # ログインページオブジェクト
│ ├── users.page.ts # ユーザーリストページオブジェクト
│ └── user-detail.page.ts # ユーザー詳細ページオブジェクト
├── tests/
│ ├── auth/
│ │ ├── login.spec.ts
│ │ └── logout.spec.ts
│ ├── users/
│ │ ├── create-user.spec.ts
│ │ ├── edit-user.spec.ts
│ │ └── list-users.spec.ts
│ └── smoke/
│ └── critical-paths.spec.ts
└── utils/
├── api-helpers.ts # テストセットアップ用の直接API呼び出し
└── test-constants.ts # 共有定数
命名規則:
- テストファイル:
<feature>.spec.ts - ページオブジェクト:
<page-name>.page.ts - フィクスチャ:
<concern>.fixture.ts - テスト名: ユーザーアクションと期待される結果を説明する人間が読める文
ページオブジェクトモデル
すべてのページはセレクターとアクションをカプセル化したページオブジェクトクラスを取得します。テストはセレクターと直接やり取りしません。
ベースページオブジェクト:
// e2e/pages/base.page.ts
import { type Page, type Locator } from "@playwright/test";
export abstract class BasePage {
constructor(protected readonly page: Page) {}
/** ページの URL に移動する。 */
abstract goto(): Promise<void>;
/** ページが完全に読み込まれるまで待機する。 */
async waitForLoad(): Promise<void> {
await this.page.waitForLoadState("networkidle");
}
/** トースト/通知メッセージを取得する。 */
get toast(): Locator {
return this.page.getByRole("alert");
}
/** ページの見出しを取得する。 */
get heading(): Locator {
return this.page.getByRole("heading", { level: 1 });
}
}
具体的なページオブジェクト:
// e2e/pages/users.page.ts
import { type Page, type Locator } from "@playwright/test";
import { BasePage } from "./base.page";
export class UsersPage extends BasePage {
// ─── Locators ─────────────────────────────────────────
readonly createButton: Locator;
readonly searchInput: Locator;
readonly userTable: Locator;
constructor(page: Page) {
super(page);
this.createButton = page.getByTestId("create-user-btn");
this.searchInput = page.getByRole("searchbox", { name: /search users/i });
this.userTable = page.getByRole("table");
}
// ─── Actions ──────────────────────────────────────────
async goto(): Promise<void> {
await this.page.goto("/users");
await this.waitForLoad();
}
async searchFor(query: string): Promise<void> {
await this.searchInput.fill(query);
// 検索結果の更新を待機 (デバウンス)
await this.page.waitForResponse("**/api/v1/users?*");
}
async clickCreateUser(): Promise<void> {
await this.createButton.click();
}
async getUserRow(email: string): Promise<Locator> {
return this.userTable.getByRole("row").filter({ hasText: email });
}
async getUserCount(): Promise<number> {
// ヘッダー行を除外するため 1 を引く
return (await this.userTable.getByRole("row").count()) - 1;
}
}
ページオブジェクトの規則:
- 1 つのページまたはメジャー UI セクションごとに 1 つのページオブジェクト
- ロケーターは public readonly プロパティ
- アクションは async メソッド
- ページオブジェクトはアサーション を含まない -- テストがアサート
- ページオブジェクトはアクション後の待機を内部で処理
セレクター戦略
優先順位 (最も高い順から最も低い順):
| 優先度 | セレクター | 例 | 使用する場合 |
|---|---|---|---|
| 1 | data-testid | getByTestId("submit-btn") | インタラクティブ要素、動的コンテンツ |
| 2 | ロール | getByRole("button", { name: /save/i }) | ボタン、リンク、見出し、入力 |
| 3 | ラベル | getByLabel("Email") | ラベル付きフォーム入力 |
| 4 | プレースホルダー | getByPlaceholder("Search...") | 検索入力 |
| 5 | テキスト | getByText("Welcome back") | 静的テキストコンテンツ |
絶対に使用しないでください:
- CSS セレクター (
.class-name,#id) -- 脆い、スタイル変更で壊れる - XPath (
//div[@class="foo"]) -- 読みにくい、非常に脆い - DOM 構造セレクター (
div > span:nth-child(2)) -- レイアウト変更で壊れる
data-testid 属性の追加:
// React コンポーネント内 -- インタラクティブ要素に data-testid を追加
<button data-testid="create-user-btn" onClick={handleCreate}>
Create User
</button>
// 規則: ケバブケース、説明的
// パターン: <action>-<entity>-<element-type>
// 例: create-user-btn, user-email-input, delete-confirm-dialog
待機戦略
ハードコードされた待機を絶対に使用しないでください:
// 悪い例: ハードコードされた待機 -- 脆い、遅い
await page.waitForTimeout(3000);
// 悪い例: スリープ
await new Promise((resolve) => setTimeout(resolve, 2000));
明示的な待機条件を使用してください:
// 良い例: 特定の要素が表示されるまで待機
await page.getByRole("heading", { name: "Dashboard" }).waitFor();
// 良い例: ナビゲーション待機
await page.waitForURL("/dashboard");
// 良い例: API レスポンス待機
await page.waitForResponse(
(response) =>
response.url().includes("/api/v1/users") && response.status() === 200,
);
// 良い例: ネットワークが安定するまで待機
await page.waitForLoadState("networkidle");
// 良い例: 要素の状態待機
await page.getByTestId("submit-btn").waitFor({ state: "visible" });
await page.getByTestId("loading-spinner").waitFor({ state: "hidden" });
自動待機: Playwright はクリック、入力などのアクション前に要素がアクション可能になるまで自動的に待機します。明示的な待機はアサーション または複雑な状態遷移に対してのみ必要です。
認証状態の再利用
テストごとにログインを避けてください。認証状態を保存して再利用してください。
認証状態を一度セットアップする:
// e2e/fixtures/auth.fixture.ts
import { test as base } from "@playwright/test";
import path from "path";
const AUTH_STATE_PATH = path.resolve("e2e/.auth/user.json");
export const setup = base.extend({});
setup("authenticate", async ({ page }) => {
// 実際のログイン実行
await page.goto("/login");
await page.getByLabel("Email").fill("testuser@example.com");
await page.getByLabel("Password").fill("TestPassword123!");
await page.getByRole("button", { name: /sign in/i }).click();
// 認証完了を待機
await page.waitForURL("/dashboard");
// サインイン状態を保存
await page.context().storageState({ path: AUTH_STATE_PATH });
});
テストで再利用:
// playwright.config.ts
export default defineConfig({
projects: [
// セットアップ プロジェクトが最初に実行され、認証状態を保存
{ name: "setup", testDir: "./e2e/fixtures", testMatch: "auth.fixture.ts" },
{
name: "chromium",
use: {
storageState: "e2e/.auth/user.json", // 認証状態を再利用
},
dependencies: ["setup"],
},
],
});
テストデータ管理
原則:
- テストは自分のデータを作成 (既存のデータに依存しない)
- テストは後片付けをする (または API を使用してリセット)
- UI インタラクションではなく、セットアップに API 呼び出しを使用 (高速で信頼性が高い)
テストデータ用の API ヘルパー:
// e2e/utils/api-helpers.ts
import { type APIRequestContext } from "@playwright/test";
export class TestDataAPI {
constructor(private request: APIRequestContext) {}
async createUser(data: { email: string; displayName: string }) {
const response = await this.request.post("/api/v1/users", { data });
return response.json();
}
async deleteUser(userId: number) {
await this.request.delete(`/api/v1/users/${userId}`);
}
async createOrder(userId: number, items: Array<Record<string, unknown>>) {
const response = await this.request.post("/api/v1/orders", {
data: { user_id: userId, items },
});
return response.json();
}
}
テストでの使用:
test("edit user name", async ({ page, request }) => {
const api = new TestDataAPI(request);
// セットアップ: API 経由でユーザーを作成 (高速)
const user = await api.createUser({
email: "edit-test@example.com",
displayName: "Before Edit",
});
try {
// テスト: UI 経由で編集
const usersPage = new UsersPage(page);
await usersPage.goto();
// ... UI 経由で編集を実行 ...
} finally {
// クリーンアップ: テストデータを削除
await api.deleteUser(user.id);
}
});
脆いテストのデバッグ
1. 失敗時にトレースビューアーを使用:
// playwright.config.ts
use: {
trace: "on-first-retry", // リトライ時のみトレース キャプチャ
}
トレースを表示: npx playwright show-trace trace.zip
2. デバッグ用に headed モードで実行:
npx playwright test --headed --debug tests/users/create-user.spec.ts
3. 脆いテストの一般的な原因:
| 原因 | 修正 |
|---|---|
| ハードコードされた待機 | 明示的な待機条件を使用 |
| 共有テストデータ | 各テストが独自のデータを作成 |
| アニメーション干渉 | config で animations: "disabled" を設定 |
| 競合状態 | アサーション前に API レスポンス待機 |
| ビューポート依存の動作 | config で明示的なビューポートを設定 |
| テスト間のセッションリーク | storageState を正しく使用、クッキーを削除 |
4. リトライ戦略:
// playwright.config.ts
export default defineConfig({
retries: process.env.CI ? 2 : 0, // CI でのみリトライ
});
CI 設定
# .github/workflows/e2e.yml
name: E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Start application
run: |
docker compose up -d
npx wait-on http://localhost:3000 --timeout 60000
- name: Run E2E tests
run: npx playwright test
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 14
- name: Upload traces on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-traces
path: test-results/
scripts/run-e2e-with-report.sh を使用して、ローカルで HTML レポート出力を指定して Playwright を実行します。
例
注釈付きページオブジェクトクラスは references/page-object-template.ts を参照してください。
注釈付き E2E テストは references/e2e-test-template.ts を参照してください。
本番環境 Playwright 設定は references/playwright-config-example.ts を参照してください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- hieutrtr
- リポジトリ
- hieutrtr/ai1-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/hieutrtr/ai1-skills / ライセンス: MIT
関連スキル
agent-browser
AI エージェント向けのブラウザ自動化 CLI です。ウェブサイトとの対話が必要な場合に使用します。ページ遷移、フォーム入力、ボタンクリック、スクリーンショット取得、データ抽出、ウェブアプリのテスト、ブラウザ操作の自動化など、あらゆるブラウザタスクに対応できます。「ウェブサイトを開く」「フォームに記入する」「ボタンをクリックする」「スクリーンショットを取得する」「ページからデータを抽出する」「このウェブアプリをテストする」「サイトにログインする」「ブラウザ操作を自動化する」といった要求や、プログラマティックなウェブ操作が必要なタスクで起動します。
anyskill
AnySkill — あなたのプライベート・スキルクラウド。GitHubを基盤としたリポジトリからエージェントスキルを管理、同期、動的にロードできます。自然言語でクラウドスキルを検索し、オンデマンドでプロンプトを自動ロード、カスタムスキルのアップロードと共有、スキルバンドルの一括インストールが可能です。OpenClaw、Antigravity、Claude Code、Cursorに対応しています。
engram
AIエージェント向けの永続的なメモリシステムです。バグ修正、意思決定、発見、設定変更の後はmem_saveを使用してください。ユーザーが「覚えている」「記憶している」と言及した場合、または以前のセッションと重複する作業を開始する際はmem_searchを使用します。セッション終了前にmem_session_summaryを使用して、コンテキストを保持してください。
skyvern
AI駆動のブラウザ自動化により、任意のウェブサイトを自動化できます。フォーム入力、データ抽出、ファイルダウンロード、ログイン、複数ステップのワークフロー実行など、ユーザーがウェブサイトと連携する必要があるときに使用します。Skyvernは、LLMとコンピュータビジョンを活用して、未知のサイトも自動操作可能です。Python SDK、TypeScript SDK、REST API、MCPサーバー、またはCLIを通じて統合できます。
pinchbench
PinchBenchベンチマークを実行して、OpenClawエージェントの実世界タスクにおけるパフォーマンスを評価できます。モデルの機能テスト、モデル間の比較、ベンチマーク結果のリーダーボード提出、またはOpenClawのセットアップがカレンダー、メール、リサーチ、コーディング、複数ステップのワークフローにどの程度対応しているかを確認する際に使用します。
openui
OpenUIとOpenUI Langを使用してジェネレーティブUIアプリを構築できます。これらはLLM生成インターフェースのためのトークン効率的なオープン標準です。OpenUI、@openuidev、ジェネレーティブUI、LLMからのストリーミングUI、AI向けコンポーネントライブラリ、またはjson-render/A2UIの置き換えについて述べる際に使用します。スキャフォルディング、defineComponent、システムプロンプト、Renderer、およびOpenUI Lang出力のデバッグに対応しています。