jpskill.com
🛠️ 開発・MCP コミュニティ

testing-e2e

システム全体に及ぶエンドツーエンドテストの作成、レビュー、リファクタリングを支援し、ユーザーシナリオ、ヘルパー関数、自動リトライ可能なアサーションなどを扱い、堅牢なテストを構築するSkill。

📜 元の英語説明(参考)

Expert guidance for writing resilient end-to-end tests that span multiple processes and components. Use this skill when reviewing, writing, or refactoring system-wide e2e tests. Covers user-facing scenarios, helper functions, data factories, ARIA-based selectors, and auto-retriable assertions. NOT for unit/integration tests - use testing-unit-integration skill instead. See references/playwright-resilience.md for detailed selector patterns.

🇯🇵 日本人クリエイター向け解説

一言でいうと

システム全体に及ぶエンドツーエンドテストの作成、レビュー、リファクタリングを支援し、ユーザーシナリオ、ヘルパー関数、自動リトライ可能なアサーションなどを扱い、堅牢なテストを構築するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o testing-e2e.zip https://jpskill.com/download/10058.zip && unzip -o testing-e2e.zip && rm testing-e2e.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10058.zip -OutFile "$d\testing-e2e.zip"; Expand-Archive "$d\testing-e2e.zip" -DestinationPath $d -Force; ri "$d\testing-e2e.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して testing-e2e.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → testing-e2e フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

End-to-End Testing のベストプラクティス

複数のプロセスにまたがるシステム全体の E2E テストに関する専門家によるガイダンスです。

範囲: システム全体の End-to-End テストのみ。モックされたバックエンドを使用したユニットテスト、統合テスト、コンポーネントテストには使用しないでください。代わりに testing-unit-integration skill を使用してください。

注意: E2E テストは複雑で、時間がかかり、数が限られています。ユニットテストと比較して、読みやすさの基準が緩和され、ベストプラクティスの厳格さが低くても許容されます。

コア・ルール

自己完結型テスト (重要)

  • A.8 各テストは自己完結しており、他のテストの状態や成果物に依存しません。
  • A.11 beforeEach またはテスト中に作成された、専用の新しいデータから開始します。
  • A.14 実際のユーザーシナリオをテストし、実際のユーザーの行動をシミュレートします。
  • A.35 自動クリーンアップによる包括的なテストデータ管理を実装します。

ユーザー向けのロケーター (重要)

  • A.17 ARIA ベースのロケーターのみ: getByRolegetByLabelgetByText
  • A.17 CSS セレクター、test ID、xpath、$、または実装固有のロケーターは使用しないでください。
  • A.40 位置セレクターは使用しないでください: nth(i)、first()、last()

境界

  • A.23 アプリケーションの境界内でのみアクセスし、テストします。
  • A.48 外部システムに近づいたり、アサートしたりしないでください。ナビゲーションが発生したことをアサートします。

構造

  • A.5 タイトルは、ステークホルダー、アクション、期待値を含むフローを要約します。
  • A.26 最大 15 ステートメント - 複数ステップの操作はヘルパー関数にカプセル化します。

ヘルパー関数パターン

各ヘルパーは、低レベルのインタラクションではなく、ユーザーのジャーニーの重要な部分を表します。

// ✅ GOOD - セマンティックヘルパー
async function loginAsAdmin(page: Page): Promise<void>
async function addProductToCart(page: Page, product: Product): Promise<void>
async function completeCheckout(page: Page): Promise<OrderDetails>
async function verifyOrderConfirmationEmailSent(email: string, order: OrderDetails): Promise<void>

// ❌ BAD - 低レベルのインタラクションが公開されている
async function clickButton(page: Page, name: string): Promise<void>
async function fillInput(page: Page, selector: string, value: string): Promise<void>

待機戦略

  • A.44 時間ベースの待機は使用しないでください: setTimeoutpage.waitForTimeout()
  • A.44 waitForSelector は使用しないでください - テストを実装に結合します。
  • A.20 自動再試行可能なアサーション (web-first アサーション) を使用します。
// ❌ BAD
await page.waitForTimeout(3000)
await page.waitForSelector('.form-loaded')

// ✅ GOOD - 自動再試行可能なアサーション
await expect(page.getByText(/success/i)).toBeVisible()
await expect(page).toHaveURL(/\/dashboard/)

データファクトリー

  • A.52 buildEntity() 関数を使用したファクトリーファイルからのデータ
  • A.52 ファクトリーはデフォルト値を返しますが、フィールドのオーバーライドを許可します。
  • A.55 ダミーの値ではなく、faker を使用して意味のあるドメインデータを使用します。
import { faker } from "@faker-js/faker";
import { Customer } from "../types";

export function buildCustomer(overrides: Partial<Customer> = {}): Customer {
  return {
    email: faker.internet.email(),
    name: faker.person.fullName(),
    address: faker.location.streetAddress(),
    ...overrides,
  };
}

アサーション

  • A.58 カスタムコーディング、ループ、Array.prototype は使用しないでください。組み込みの expect API を使用します。
  • A.62 最小限のアサーション - 1 つの強力なアサーションですべての問題をキャッチします。
  • A.65 失敗時に完全な差分を表示するマッチャーを使用します。

強力なアサーション

// ❌ WEAK - 複数の冗長なアサーション
expect(response).not.toBeNull()
expect(Array.isArray(response)).toBe(true)
expect(response.length).toBe(2)

// ✅ STRONG - 単一のアサーションですべてをキャッチ
expect(response).toEqual([{id: '123'}, {id: '456'}])

ナビゲーションテスト

E2E テストは、モックされたユニットテスト/統合テストでは見逃されるナビゲーションのバグをキャッチします。

  • A.50 ナビゲーションの結果 (宛先ページがレンダリングされる) をアサートします。navigate() が呼び出されたことをアサートしないでください。
  • A.52 テストルーターの構成は、本番ルーターと一致する必要があります。モックされたルートではなく、実際のルートを使用します。
  • A.54 ハードコードされたパス文字列ではなく、ルート定数を使用します。タイプミスやドリフトを防ぎます。
  • A.56 ナビゲーションアクション後に URL が期待される値に変更されたことを確認します。
  • A.58 E2E テストはセーフティネットです。モックされたテストでは見逃されるコントラクトとナビゲーションのバグをキャッチします。
// ❌ BAD - navigate 関数が呼び出されたことをテストする (実装の詳細)
expect(mockNavigate).toHaveBeenCalledWith('/users/123');

// ❌ BAD - ハードコードされたパス文字列 (タイプミスはキャッチされない)
await page.goto('/usres/123');  // タイプミス! ユニットテストでは何も起こらずに失敗する

// ✅ GOOD - 結果をアサートする (ユーザーがページを見る)
await expect(page.getByRole('heading', { name: /user profile/i })).toBeVisible();

// ✅ GOOD - URL が変更されたことを確認する
await expect(page).toHaveURL(/\/users\/\d+/);

// ✅ GOOD - ルート定数を使用する (共有定数ファイルから)
import { ROUTES } from '@/constants/routes';
await page.goto(ROUTES.USER_PROFILE.replace(':id', '123'));

E2E がユニットテストで見逃すものをキャッチする理由

バグの種類 ユニットテスト (モック) E2E テスト
navigate('/usres') のルートのタイプミス ❌ 合格 (モックは検証しない) ✅ 失敗 (404 ページ)
ルートパラメータの不一致 (email vs id) ❌ 合格 (モックは何でも受け入れる) ✅ 失敗 (間違ったページがロードされる)
ルーター構成にルートがない ❌ 合格 (モックはルーターを使用しない) ✅ 失敗 (404 またはエラー)
バックエンドコントラクトの変更 ❌ 合格 (モックはファンタジーデータを返す) ✅ 失敗 (実際の API が異なる)

最大限のカバレッジ、最小限のテスト

E2E テストはコストがかかります。テストごとの価値を最大化します。

  • 各テストは完全なユーザーのジャーニーをカバーします。
  • 同じフローに属する検証ステップを組み合わせます。
  • クリティカルパスとリスクの高いシナリオに焦点を当てます。
  • ヘルパー関数を使用して、複雑さにもかかわらずテストを読みやすく保ちます。

BAD E2E テストの例

test('Should purchase item', async ({ page }) => { // ❌ A.5 - 曖昧なタイトル
  await page.goto('/products') // ❌ A.8, A.11 - 既存のセッションを想定

  await page.getByText('iPhone 15 Pro').click() // ❌ A.8 - 特定の製品を想定
  await page.locator('#add-to-cart-btn').click() // ❌ A.17 - CSS セレクター
  await page.goto('/checkout')

  await p
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

End-to-End Testing Best Practices

Expert guidance for system-wide e2e tests spanning multiple processes.

Scope: System-wide end-to-end tests only. NOT for unit, integration, or component tests with mocked backends - use testing-unit-integration skill instead.

Note: E2E tests are complex, slow, and limited in quantity - relaxed readability standards and less stringent best practices are acceptable compared to unit tests.

Core Rules

Self-Contained Tests (Critical)

  • A.8 Each test is self-contained, never relies on other tests' state or artifacts
  • A.11 Start with fresh dedicated data created in beforeEach or during test
  • A.14 Test real user scenarios, simulate actual user behavior
  • A.35 Implement comprehensive test data management with automatic cleanup

User-Facing Locators (Critical)

  • A.17 Only ARIA-based locators: getByRole, getByLabel, getByText
  • A.17 NO CSS selectors, test IDs, xpath, $, or implementation-specific locators
  • A.40 No positional selectors: nth(i), first(), last()

Boundaries

  • A.23 Only visit and test within application boundaries
  • A.48 Don't approach or assert on external systems - assert navigation happened

Structure

  • A.5 Title summarizes flow with stakeholder, action, expectations
  • A.26 Max 15 statements - encapsulate multi-step operations in helper functions

Helper Functions Pattern

Each helper represents a meaningful part of user's journey, not low-level interactions:

// ✅ GOOD - Semantic helpers
async function loginAsAdmin(page: Page): Promise<void>
async function addProductToCart(page: Page, product: Product): Promise<void>
async function completeCheckout(page: Page): Promise<OrderDetails>
async function verifyOrderConfirmationEmailSent(email: string, order: OrderDetails): Promise<void>

// ❌ BAD - Low-level interactions exposed
async function clickButton(page: Page, name: string): Promise<void>
async function fillInput(page: Page, selector: string, value: string): Promise<void>

Waiting Strategies

  • A.44 No time-based waiting: setTimeout, page.waitForTimeout()
  • A.44 No waitForSelector - couples test to implementation
  • A.20 Use auto-retriable assertions (web-first assertions):
// ❌ BAD
await page.waitForTimeout(3000)
await page.waitForSelector('.form-loaded')

// ✅ GOOD - Auto-retriable assertions
await expect(page.getByText(/success/i)).toBeVisible()
await expect(page).toHaveURL(/\/dashboard/)

Data Factories

  • A.52 Data from factory files with buildEntity() functions
  • A.52 Factories return defaults but allow field overrides
  • A.55 Use meaningful domain data with faker, not dummy values
import { faker } from "@faker-js/faker";
import { Customer } from "../types";

export function buildCustomer(overrides: Partial<Customer> = {}): Customer {
  return {
    email: faker.internet.email(),
    name: faker.person.fullName(),
    address: faker.location.streetAddress(),
    ...overrides,
  };
}

Assertions

  • A.58 No custom coding, loops, Array.prototype - use built-in expect APIs
  • A.62 Minimal assertions - one strong assertion catches all issues
  • A.65 Use matchers that show full diff on failure

Strong Assertions

// ❌ WEAK - Multiple redundant assertions
expect(response).not.toBeNull()
expect(Array.isArray(response)).toBe(true)
expect(response.length).toBe(2)

// ✅ STRONG - Single assertion catches all
expect(response).toEqual([{id: '123'}, {id: '456'}])

Navigation Testing

E2E tests catch navigation bugs that mocked unit/integration tests miss.

  • A.50 Assert navigation OUTCOME (destination page renders), not that navigate() was called
  • A.52 Test router config must match production router - use real routes, not mocked
  • A.54 Use route constants, not hardcoded path strings - prevents typos and drift
  • A.56 Verify URL changed to expected value after navigation actions
  • A.58 E2E tests are the safety net - they catch contract and navigation bugs that mocked tests miss
// ❌ BAD - Testing that navigate function was called (implementation detail)
expect(mockNavigate).toHaveBeenCalledWith('/users/123');

// ❌ BAD - Hardcoded path string (typos won't be caught)
await page.goto('/usres/123');  // Typo! Would fail silently in unit test

// ✅ GOOD - Assert the OUTCOME (user sees the page)
await expect(page.getByRole('heading', { name: /user profile/i })).toBeVisible();

// ✅ GOOD - Verify URL changed
await expect(page).toHaveURL(/\/users\/\d+/);

// ✅ GOOD - Use route constants (from shared constants file)
import { ROUTES } from '@/constants/routes';
await page.goto(ROUTES.USER_PROFILE.replace(':id', '123'));

Why E2E Catches What Unit Tests Miss

Bug Type Unit Test (Mocked) E2E Test
Route typo in navigate('/usres') ❌ Passes (mock doesn't validate) ✅ Fails (404 page)
Route param mismatch (email vs id) ❌ Passes (mock accepts anything) ✅ Fails (wrong page loads)
Missing route in router config ❌ Passes (mock doesn't use router) ✅ Fails (404 or error)
Backend contract change ❌ Passes (mock returns fantasy data) ✅ Fails (real API different)

Maximum Coverage, Minimal Tests

E2E tests are expensive - maximize value per test:

  • Each test covers a complete user journey
  • Combine verification steps that belong to same flow
  • Focus on critical paths and high-risk scenarios
  • Use helper functions to keep tests readable despite complexity

BAD E2E Test Example

test('Should purchase item', async ({ page }) => { // ❌ A.5 - vague title
  await page.goto('/products') // ❌ A.8, A.11 - assumes existing session

  await page.getByText('iPhone 15 Pro').click() // ❌ A.8 - assumes specific product
  await page.locator('#add-to-cart-btn').click() // ❌ A.17 - CSS selector
  await page.goto('/checkout')

  await page.waitForSelector('.form-loaded') // ❌ A.44 - implementation detail
  await page.locator('#email').fill('foo@test.com') // ❌ A.17, A.55 - CSS + dummy data
  await page.locator('button').last().click() // ❌ A.17, A.40 - positional

  const cartItems = await page.locator('.cart-item').all() // ❌ A.58 - custom loop
  for (const item of cartItems) expect(await item.isVisible()).toBe(true) // ❌ A.58

  await page.goto('https://stripe.com/confirm') // ❌ A.23 - external system
  expect(await page.evaluate(() => localStorage.getItem('orderId'))).toBeTruthy() // ❌ A.14 - implementation
})

GOOD E2E Test Example

test('The user can purchase an item and post-purchase experience is valid', async ({ page }) => {
  // Arrange - Create fresh test data
  const customer = await saveNewCustomer(buildCustomer())
  const product = await saveNewProduct(buildProduct())

  // Act - User journey through semantic helpers
  await navigateToProductsPage(page)
  await selectProduct(page, product)
  await goToCheckout(page)
  await fillCustomerDetails(page, customer)
  await fillPaymentDetails(page, buildCreditCard())

  // Assert - Verify outcomes
  const orderDetails = await submitOrderAndVerifySuccessPage(page)
  await verifyOrderConfirmationEmailSent(customer.email, orderDetails)
  await verifySupplyRequestWasCreated(customer.email, orderDetails)
})

Selector Reference

For detailed selector patterns, page object best practices, and resilience strategies, see:

references/playwright-resilience.md - Comprehensive guide including:

  • Selector priority table (getByRole > getByLabel > getByPlaceholder...)
  • Bad vs Good patterns for forms, buttons, headings, waiting
  • When to use data-testid (error messages, toasts, loading indicators)
  • Page Object best practices
  • Text matching strategies (regex, partial, exact)
  • Quick reference for all element types

Rule Violation Reporting

When reviewing e2e tests, report violations as:

Line X: Violates [RULE_NUMBER] - [Brief explanation]

Example:

Line 8: Violates A.17 - Uses CSS selector '#email', should use getByLabel('Email')
Line 12: Violates A.44 - Uses waitForTimeout, should use auto-retriable assertion
Line 15: Violates A.23 - Navigates to external domain stripe.com