e2e-outside-in-test-generator について
e2e-outside-in-test-generatorは、フルスタックアプリ向けのPlaywrightエンドツーエンドテストを生成するスキルです。ブラウザーからバックエンドまでのフローを検証したい開発・テストチーム向けで、アプリ分析、テスト生成、失敗の反復修正を行います。
主な機能
- 01フロントエンドのルートとバックエンドAPIを分析
- 02Playwright設定とテストヘルパーを生成
- 037種類のエンドツーエンドテストを作成
- 04失敗を分析して反復的に修正
- 05カバレッジレポートと推奨事項を作成
活用シーン
- 01フルスタックアプリのブラウザテストを生成
- 02重要なユーザーフローと境界条件を検証
- 03大規模なリファクタリング前に回帰テストを追加
- 04デプロイ前にエンドツーエンドの動作を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rysweet/ amplihack --skill e2e-outside-in-test-generatorインストール後、Claude Code で/e2e-outside-in-test-generatorを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CLEANUP_REPORT.mdIMPLEMENTATION.mdREADME.mdexamples.mdgenerator/__init__.pyapi_test_generator.pyapp_type_detector.pycli_test_generator.pycoverage_audit.pyinfrastructure_setup.pymcp_test_generator.pymodels.pyorchestrator.pysecurity.pystack_detector.pytemplate_manager.pygenerator/templates/api_endpoint.templateapi_streaming.templateapi_workflow.templatecli_command.templatecli_error_handling.templatecli_smoke.templatecomponent_interaction.templateform_interaction.templatekeyboard_shortcuts.templatemcp_tool.templatemcp_workflow.templatepwa_basics.templateresponsive.templatesmoke.template詳細
E2E Outside-In テストジェネレーター
Outside-in テスト手法を使用して、フルスタックアプリケーション向けの包括的なエンドツーエンド Playwright テストを自動生成します。7 カテゴリーにわたって 40 件以上のテストを生成し、実際のバグを見つけ、反復的な修正ループで検証します。
LEVEL 1: クイックスタート
目的
このスキルはフルスタックアプリケーションを分析し、次のような完全な Playwright テストスイートを生成します。
- ブラウザーからバックエンドまでのユーザージャーニーを検証
- 体系的な調査を通じて実際のバグを発見
- 不安定さのない、決定論的な実行
- すぐに CI に統合可能
使用するタイミング
次の場合にこのスキルを有効にします。
- フルスタックアプリの包括的なブラウザーテストが必要な場合
- 重要なユーザーフローをエンドツーエンドで検証したい場合
- 大規模なリファクタリングの前にリグレッションテストを追加したい場合
- 本番環境へのデプロイを準備している場合
要件:プロジェクトにフロントエンド(Next.js、React、Vue、Angular)とバックエンド API の両方が必要です。
クイックスタート
# In your project root
$ claude
> add e2e tests
このスキルは次の処理を自動的に行います。
- アプリケーションを分析します(ルート、API エンドポイント、データベーススキーマ)
- インフラストラクチャをセットアップします(Playwright 設定、テストヘルパー、シードデータ)
- 7 カテゴリーにわたって40 件以上のテストを生成します
- テストを実行し、失敗を修正します(最大 5 回の反復)
- カバレッジと推奨事項をレポートします
期待される出力
e2e/
├── playwright.config.ts # Playwright configuration
├── test-helpers/
│ ├── auth.ts # Authentication helpers
│ ├── navigation.ts # Navigation helpers
│ ├── assertions.ts # Custom assertions
│ └── data-setup.ts # Test data management
├── fixtures/
│ ├── users.json # Test user data
│ ├── products.json # Test product data
│ └── seed.sql # Database seed script
├── happy-path/
│ ├── user-registration.spec.ts
│ ├── user-login.spec.ts
│ └── checkout-flow.spec.ts
├── edge-cases/
│ ├── invalid-inputs.spec.ts
│ └── boundary-conditions.spec.ts
├── error-handling/
│ ├── network-failures.spec.ts
│ └── validation-errors.spec.ts
├── performance/
│ ├── page-load-times.spec.ts
│ └── api-response-times.spec.ts
├── security/
│ ├── unauthorized-access.spec.ts
│ └── xss-protection.spec.ts
├── accessibility/
│ ├── keyboard-navigation.spec.ts
│ └── screen-reader.spec.ts
└── integration/
├── database-persistence.spec.ts
└── api-integration.spec.ts
Total: 42 tests across 7 categories
テストの実行
# Run all tests
npx playwright test
# Run specific category
npx playwright test e2e/happy-path
# Run in headed mode
npx playwright test --headed
# Run in debug mode
npx playwright test --debug
成功基準
生成後、テストスイートは次の基準を達成します。
- ✓ 7 カテゴリーすべてにわたる40 件以上のテスト
- ✓ 修正ループ後の合格率 100%
- ✓ 実行時間の合計が2 分未満
- ✓ 生成中に実際のバグを 1 件以上発見
- ✓ 不安定さゼロ(決定論的なテストデータ)
LEVEL 2: 全機能
5 つのフェーズ
graph LR
A[Phase 1: Analysis] --> B[Phase 2: Infrastructure]
B --> C[Phase 3: Generation]
C --> D[Phase 4: Fix Loop]
D --> E[Phase 5: Coverage Audit]
フェーズ 1:スタック分析
このスキルは、アプリケーションを詳細に分析します。
フロントエンド分析:
- フレームワークを検出します(Next.js、React、Vue、Angular)
- ルートとページをマッピングします
- ナビゲーションパターンを特定します
- インタラクティブな要素を抽出します
バックエンド分析:
- API エンドポイントを検出します(REST/GraphQL)
- データモデルとリレーションシップをマッピングします
- 認証方式を特定します
- バリデーションルールを検出します
データベース分析:
- スキーマとリレーションシップを抽出します
- 必須フィールドを特定します
- 外部キー制約を判定します
- enum 型をマッピングします
分析出力の例:
StackConfig(
frontend_framework="nextjs",
frontend_dir="app/",
backend_framework="fastapi",
api_base_url="http://localhost:8000/api",
database_type="postgresql",
auth_mechanism="jwt",
routes=[
Route(path="/", component="Home"),
Route(path="/login", component="Login"),
Route(path="/products", component="ProductList"),
Route(path="/products/:id", component="ProductDetail"),
Route(path="/checkout", component="Checkout")
],
api_endpoints=[
APIEndpoint(path="/auth/login", method="POST"),
APIEndpoint(path="/auth/register", method="POST"),
APIEndpoint(path="/products", method="GET"),
APIEndpoint(path="/products/:id", method="GET"),
APIEndpoint(path="/orders", method="POST")
],
models=[
Model(name="User", fields=["id", "email", "password"]),
Model(name="Product", fields=["id", "name", "price", "stock"]),
Model(name="Order", fields=["id", "user_id", "product_id", "quantity"])
]
)
フェーズ 2:インフラストラクチャのセットアップ
テストに必要なインフラストラクチャ一式を生成します。
Playwright の設定:
// e2e/playwright.config.ts
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e",
fullyParallel: false, // CRITICAL: workers=1 for deterministic test data
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1, // NEVER > 1 to prevent data races
reporter: "html",
use: {
baseURL: "http://localhost:3000",
trace: "on-first-retry",
},
projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
webServer: {
command: "npm run dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
});
テストヘルパー:
// e2e/test-helpers/auth.ts
export async function login(page: Page, email: string, password: string) {
await page.goto("/login");
await page.getByRole("textbox", { name: /email/i }).fill(email);
await page.getByRole("textbox", { name: /password/i }).fill(password);
await page.getByRole("button", { name: /sign in/i }).click();
await page.waitForURL("/dashboard");
}
export async function logout(page: Page) {
await page.getByRole("button", { name: /logout/i }).click();
await page.waitForURL("/");
}
シードデータ:
// e2e/fixtures/users.json
[
{
"email": "[email protected]",
"password": "Test123!", // pragma: allowlist secret
"name": "Test User",
"role": "customer"
},
{
"email": "[email protected]",
"password": "Admin123!", // pragma: allowlist secret
"name": "Admin User",
"role": "admin"
}
]
フェーズ 3:テスト生成
必須の 7 カテゴリーにわたってテストを生成します。
1. 正常系テスト(重要なユーザージャーニー)
// e2e/happy-path/user-registration.spec.ts
import { test, expect } from "@playwright/test";
test("user can register with valid credentials", async ({ page }) => {
await page.goto("/register");
await page.getByRole("textbox", { name: /email/i }).fill("[email protected]");
await page.getByRole("textbox", { name: /password/i }).fill("Password123!");
await page.getByRole("textbox", { name: /confirm password/i }).fill("Password123!");
await page.getByRole("button", { name: /create account/i }).click();
await expect(page).toHaveURL("/dashboard");
await expect(page.getByText(/welcome/i)).toBeVisible();
});
2. エッジケーステスト(境界条件)
// e2e/edge-cases/boundary-conditions.spec.ts
test("rejects password that is too short", async ({ page }) => {
await page.goto("/register");
await page.getByRole("textbox", { name: /email/i }).fill("[email protected]");
await page.getByRole("textbox", { name: /password/i }).fill("123"); // Too short
await page.getByRole("button", { name: /create account/i }).click();
await expect(page.getByText(/password must be at least 8 characters/i)).toBeVisible();
});
3. エラーハンドリングテスト(失敗シナリオ)
// e2e/error-handling/network-failures.spec.ts
test("shows error when API is unavailable", async ({ page, context }) => {
// Simulate network failure
await context.route("**/api/**", (route) => route.abort());
await page.goto("/products");
await expect(page.getByText(/unable to load products/i)).toBeVisible();
await expect(page.getByRole("button", { name: /retry/i })).toBeVisible();
});
4. パフォーマンステスト(速度の検証)
// e2e/performance/page-load-times.spec.ts
test("homepage loads in under 2 seconds", async ({ page }) => {
const startTime = Date.now();
await page.goto("/");
await page.waitForLoadState("networkidle");
const loadTime = Date.now() - startTime;
expect(loadTime).toBeLessThan(2000);
});
5. セキュリティテスト(認可/XSS)
// e2e/security/unauthorized-access.spec.ts
test("redirects unauthenticated users from protected routes", async ({ page }) => {
await page.goto("/dashboard");
await expect(page).toHaveURL("/login");
await expect(page.getByText(/please sign in/i)).toBeVisible();
});
6. アクセシビリティテスト(WCAG 準拠)
// e2e/accessibility/keyboard-navigation.spec.ts
import { test, expect } from "@playwright/test";
test("login form is fully keyboard accessible", async ({ page }) => {
await page.goto("/login");
await page.keyboard.press("Tab"); // Focus email
await page.keyboard.type("[email protected]");
await page.keyboard.press("Tab"); // Focus password
await page.keyboard.type("Test123!");
await page.keyboard.press("Tab"); // Focus submit button
await page.keyboard.press("Enter"); // Submit form
await expect(page).toHaveURL("/dashboard");
});
7. 統合テスト(データベース/API)
// e2e/integration/database-persistence.spec.ts
test("product order persists across sessions", async ({ page, context }) => {
// Create order
await login(page, "[email protected]", "Test123!");
await page.goto("/products/123");
await page.getByRole("button", { name: /add to cart/i }).click();
await page.goto("/checkout");
await page.getByRole("button", { name: /place order/i }).click();
const orderNumber = await page.getByText(/order #\d+/).textContent();
// Logout and login again
await logout(page);
await login(page, "[email protected]", "Test123!");
// Verify order exists
await page.goto("/orders");
await expect(page.getByText(orderNumber!)).toBeVisible();
});
フェーズ 4:修正ループ
反復的なデバッグを通じて、失敗したテストを自動修正します。
修正ループのプロセス:
- テストを実行 → 失敗を収集
- 失敗を分析 → 問題を分類(ロケーター、タイミング、データ、ロジック)
- 修正を適用 → 問題の種類に応じてテストを更新
- テストを再実行 → 修正を検証
- 繰り返し → 最大 5 回の反復
一般的な修正パターン:
// Before: Flaky locator
await page.click(".submit-button");
// After: Role-based locator
await page.getByRole("button", { name: /submit/i }).click();
// Before: Race condition
await page.click("#login");
await page.fill("#email", "[email protected]");
// After: Wait for navigation
await page.click("#login");
await page.waitForLoadState("networkidle");
await page.fill("#email", "[email protected]");
// Before: Hardcoded data
await page.fill("#quantity", "5");
// After: Dynamic data from fixtures
const product = await getTestProduct();
await page.fill("#quantity", product.minQuantity.toString());
修正ループの結果:
Iteration 1: 42 tests, 8 failures
- Fixed 5 locator issues
- Fixed 2 timing issues
- Fixed 1 data issue
Iteration 2: 42 tests, 3 failures
- Fixed 2 locator issues
- Fixed 1 timing issue
Iteration 3: 42 tests, 0 failures ✓
Fix loop completed in 3 iterations.
フェーズ 5:カバレッジ監査
包括的なカバレッジレポートと推奨事項を生成します。
カバレッジレポート:
## Test Coverage Report
### Tests Generated: 42
- Happy Path: 12 tests
- Edge Cases: 8 tests
- Error Handling: 6 tests
- Performance: 4 tests
- Security: 5 tests
- Accessibility: 4 tests
- Integration: 3 tests
### Routes Covered: 12/15 (80%)
Uncovered routes:
- /admin/settings (requires admin role)
- /api/webhooks/\* (external integrations)
- /debug/\* (development-only)
### API Endpoints Covered: 18/22 (82%)
Uncovered endpoints:
- POST /api/admin/users (admin-only)
- DELETE /api/products/:id (soft delete, needs verification)
- GET /api/analytics (complex aggregations)
- POST /api/webhooks/stripe (external trigger)
### Bugs Found: 2
1. **CRITICAL**: Login form allows SQL injection via email field
- Location: app/login/page.tsx:45
- Test: e2e/security/sql-injection.spec.ts
2. **MEDIUM**: Checkout fails with international phone numbers
- Location: lib/validation.ts:12
- Test: e2e/edge-cases/international-phone.spec.ts
### Recommendations
1. Add admin-role tests (requires admin user setup)
2. Add webhook integration tests (requires test webhook server)
3. Add load testing for high-traffic endpoints
4. Increase accessibility coverage (current: 9%, target: 15%)
カスタマイズオプション
カスタムロケーター戦略:
// e2e/playwright.config.ts
export default defineConfig({
use: {
// Prioritize test IDs
testIdAttribute: "data-testid",
},
});
カスタムテストカテゴリー:
# Add custom category during generation
custom_categories = [
"happy-path",
"edge-cases",
"error-handling",
"performance",
"security",
"accessibility",
"integration",
"custom-business-rules" # Your custom category
]
カスタムシードデータ:
// e2e/fixtures/custom-data.json
{
"scenarios": [
{
"name": "bulk-order",
"users": [...],
"products": [...],
"expected_discount": 0.15
}
]
}
設定
環境変数:
# .env.test
DATABASE_URL=postgresql://test:test@localhost:5432/testdb # pragma: allowlist secret
API_BASE_URL=http://localhost:8000/api
FRONTEND_URL=http://localhost:3000
[email protected]
ADMIN_PASSWORD=Admin123! # pragma: allowlist secret
Playwright オプション:
// e2e/playwright.config.ts
export default defineConfig({
// Test execution
workers: 1, // MANDATORY: prevents data races
fullyParallel: false, // Sequential execution
retries: process.env.CI ? 2 : 0,
// Timeouts
timeout: 30000, // Per-test timeout
expect: { timeout: 5000 }, // Assertion timeout
// Artifacts
use: {
screenshot: "only-on-failure",
video: "retain-on-failure",
trace: "on-first-retry",
},
});
LEVEL 3: 高度な使い方
他のスキルとの連携
test-gap-analyzer との連携:
> analyze test gaps and generate e2e tests to fill them
このスキルは次の処理を行います。
- test-gap-analyzer を実行して、テストでカバーされていないフローを特定します
- ギャップ分析に基づいて、テスト生成の優先順位を決定します
- リスクの高い未カバー領域に重点を置きます
shadow-testing との連携:
> generate e2e tests and run shadow testing against production
このスキルは次の処理を行います。
- 完全なテストスイートを生成します
- 本番環境のミラーを使って shadow-testing を設定します
- 両方の環境でテストを実行します
- 差異を報告します
qa-team methodology(以前は outside-in-testing)との連携:
このスキルは、次のとおり本質的にアウトサイドインテストに従っています。
- ユーザー向けの UI から開始します
- すべてのレイヤー(UI → API → DB)を通してテストします
- エンドツーエンドの契約への準拠を検証します
- モックを使いません(実際に統合します)
カスタムテンプレートの開発
テンプレートの構成:
# e2e/templates/custom-template.py
CUSTOM_TEST_TEMPLATE = """
import {{ test, expect }} from '@playwright/test';
import {{ {helpers} }} from '../test-helpers/{helper_module}';
test.describe('{feature_name}', () => {{
test.beforeEach(async ({{ page }}) => {{
// Setup
{setup_code}
}});
test('{test_description}', async ({{ page }}) => {{
// Arrange
{arrange_code}
// Act
{act_code}
// Assert
{assert_code}
}});
}});
"""
カスタムテンプレートの使用:
from e2e_outside_in_test_generator.template_manager import TemplateManager
template_manager = TemplateManager()
template_manager.register_template("custom-flow", CUSTOM_TEST_TEMPLATE)
test_code = template_manager.render("custom-flow", {
"feature_name": "Payment Processing",
"helpers": "login, checkout",
"helper_module": "payment",
"setup_code": "await setupPaymentGateway();",
"arrange_code": "const cart = await createCart();",
"act_code": "await processPayment(cart);",
"assert_code": "await expect(page.getByText(/payment successful/i)).toBeVisible();"
})
高度なロケーター戦略
優先順位:
- ロールベース(推奨):
getByRole('button', { name: /submit/i }) - ユーザーに表示されるテキスト:
getByText(/welcome/i) - テスト ID:
getByTestId('submit-button') - CSS セレクター(最終手段):
locator('.submit-button')
カスタムロケータービルダー:
// e2e/test-helpers/locators.ts
export function findByDataAttribute(page: Page, attr: string, value: string) {
return page.locator(`[data-${attr}="${value}"]`);
}
export function findByAriaLabel(page: Page, label: string) {
return page.locator(`[aria-label*="${label}" i]`);
}
// Usage in tests
await findByDataAttribute(page, "action", "submit").click();
パフォーマンスの最適化
テストの実行時間:
// Group fast tests together
test.describe("Quick smoke tests", () => {
test("homepage renders", async ({ page }) => {
/* <1s */
});
test("navigation works", async ({ page }) => {
/* <1s */
});
});
// Isolate slow tests
test.describe("Full checkout flow @slow", () => {
test("complete purchase", async ({ page }) => {
/* 10s */
});
});
実行プロファイル:
// package.json
{
"scripts": {
"test:e2e": "playwright test",
"test:e2e:quick": "playwright test --grep-invert @slow",
"test:e2e:full": "playwright test",
"test:e2e:smoke": "playwright test e2e/happy-path"
}
}
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@v3
- uses: actions/setup-node@v3
with:
node-version: "18"
- name: Install dependencies
run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run E2E tests
run: npm run test:e2e
env:
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
- uses: actions/upload-artifact@v3
if: failure()
with:
name: playwright-report
path: playwright-report/
トラブルシューティング
よくある問題:
-
並列実行による不安定なテスト
- 原因:
workers > 1によってデータベースの競合状態が発生します - 対処法:
playwright.config.tsでworkers: 1に設定します
- 原因:
-
ロケーターのタイムアウト
- 原因:要素がまだレンダリングされていません
- 対処法:明示的な待機を追加します:
await page.waitForLoadState('networkidle')
-
テストデータの競合
- 原因:クリーンアップされていない共有テストデータベースを使用しています
- 対処法:
test.beforeEach()を使ってデータベースの状態をリセットします
-
認証の失敗
- 原因:セッションのタイムアウト、または認証情報が正しくありません
- 対処法:
e2e/fixtures/users.jsonのテスト用認証情報を確認します
デバッグコマンド:
# Run single test in headed mode
npx playwright test e2e/happy-path/login.spec.ts --headed
# Run with debugger
npx playwright test --debug
# Generate trace for failing test
npx playwright test --trace on
# View trace
npx playwright show-trace trace.zip
ベストプラクティス
- ワーカー数は 1 に保つ - 決定的なテストデータを得るため、決して増やさないでください
- ロールベースのロケーターを使う - UI の変更に対して最も耐性があります
- 小さなシードデータセットを使う - 予測可能性を保つため、最大 10~20 件にします
- 明示的に待機する -
waitForLoadState()を使って競合状態を避けます - テスト間でクリーンアップする -
beforeEachを使って状態をリセットします - テストを分離する - 各テストは独立して実行できるようにします
- 意味のあるアサーションを行う - 実装ではなくビジネスロジックをテストします
成功指標
このスキルを実装した後は、次の達成を目指してください。
- テスト数:40 件以上(7 つのカテゴリすべて)
- 合格率:修正ループ後に 100%
- 実行時間:合計 2 分未満
- バグ検出数:実際のバグを 1 件以上検出
- カバレッジ:ルートとエンドポイントの 80% 以上
- 不安定さ:0%(決定的)
- CI との連携:すべての PR でチェックが成功
関連項目:
- README.md - 開発者向けドキュメント
- examples.md - 使用例
- reference.md - API リファレンス
- patterns.md - 一般的なパターン