エンドツーエンドテスト
エンドツーエンドテストは、実際のアプリケーションをユーザーが利用するのと同じ方法でビルド・起動し、外部から操作します。ブラウザでページをクリック、シェルでCLIを実行、またはクライアントでAPIを呼び出すなどです。これにより、ユニットテストでは発見できない問題(配線、設定、ルーティング、コンポーネント間の境界)を捉えます。ただし、最も低速で脆弱なテストでもあるため、それぞれのテストがその価値を証明し、毎回同じ結果を返すように記述する必要があります。
関連スキル unit-testing は単一ユニットのテストをカバーします。ある振る舞いをどちらのレベルでもチェックできる場合は、ユニットテストでチェックしてください。
リポジトリのルールを優先する
リポジトリのE2Eフレームワーク、設定、フォルダ、フィクスチャ、スクリプトを使用してください。テストを書く前に既存のテストと設定(playwright.config.*、cypress.config.*など)を読み、それらに合わせてください。AGENTS.md、CONTRIBUTING.md、およびパッケージスクリプトには、E2Eテストの実行方法と対象が記載されています。これらがこのスキルと矛盾する場合は、リポジトリのルールが優先されます。2つ目のE2Eフレームワークは追加しないでください。
デフォルトツール
リポジトリにまだE2Eツールがない場合、ブラウザを伴うテストには Playwright (@playwright/test) を使用してください。これはバックエンドの言語を問わず、あらゆるWebアプリで機能します。ここでの例はPlaywrightを使用しますが、ルール自体はどのツールでも適用されます。
E2Eテストに値するもの
- 重要なジャーニー: サインアップ、サインイン、製品の中核タスク、チェックアウト、および本番環境で過去に障害が発生したもの。
- 統合ポイント: 実際のバックエンド、認証、またはルーティングが必要なページ。
- ジャーニーごとに1つのハッピーパス、およびユーザーが実際に遭遇するであろう失敗(間違ったパスワード、支払い拒否など)。
その他はすべてユニットテストに委譲します。すべてのバリデーションルール、すべてのフォーマットケース、計算のすべてのブランチです。すべてのケースをカバーしようとするE2Eスイートは、実行が遅くなりすぎ、信頼性も低下します。
ロケーター
ユーザーと同じ方法で要素を見つけます。これにより、テストがマークアップやスタイルの変更に対応できます。
- 優先順位(高い順): roleとアクセシブル名(
getByRole('button', { name: 'Save' }))、ラベル(getByLabel('Email'))、可視テキスト、最後の手段としてテストID(getByTestId)。 - 避けるもの: CSSクラス、XPath、
nth-child、要素の位置。これらはレイアウトが変わると壊れ、ユーザーが見ているものについて何も示しません。 - 要素をroleやラベルで見つけられない場合、それは多くの場合アクセシビリティのバグです。脆弱なセレクタに頼るのではなく、そのように指摘してください。
ウェイト
固定時間のウェイトは決して使用しないでください。sleep、waitForTimeout、setTimeout は、遅いCIマシンでは短すぎ、高速なマシンでは時間を無駄にします。
- パスするかタイムアウトするまでリトライするアサーションを使用します:
await expect(page.getByRole('alert')).toHaveText('Saved')。 - ユーザーが待つであろう条件を待ちます: 要素が表示される、URLが変わる、レスポンスを受信する(
page.waitForResponse)、ファイルが書き込まれる。 - 失敗を修正するためにタイムアウトを延長しないでください。何かが正当に時間がかかる場合は、そのステップのみタイムアウトを延長し、その理由を説明してください。
- フレームワークのチェックを無視してアクションを強制しないでください(
{ force: true })。クリックを強制する必要がある場合、何かが要素を覆っているか準備ができていない状態であり、実際のユーザーも同じ問題に直面します。
テストデータと分離
- 各テストは独自の状態を設定し、単独で、任意の順序で、他のテストと並行して実行できる必要があります。どのテストも、他のテストが先に実行されていることに依存してはいけません。
- テストごとにユニークなデータを作成(生成されたメールアドレスやIDなど)することで、並行ワーカーやリトライが衝突するのを防ぎます。
- バックドア経由で状態を設定します。API、シードスクリプト、フィクスチャを使用してユーザーやレコードを作成し、UIをクリックして作成しないでください。テスト対象のジャーニーのみがUIを通じて実行されます。
- サインインは一度行い、再利用します。セットアップステップで認証状態を保存し(Playwrightの
storageState)、すべてのテストでフォームからログインする代わりに使用します。 - 自分が所有していないもののみをフェイクします。サードパーティサービス(決済、メール、地図)はネットワーク境界で
page.route()またはローカルフェイクを使用してスタブ化します。独自のフロントエンド、バックエンド、データベースは実際のものを使い続けます。そうでなければ、それはエンドツーエンドテストとは言えません。
CLIおよびAPIのエンドツーエンドテスト
同じルールがブラウザなしでも適用されます。CLIテストはリポジトリのユニットテストランナー(JavaScriptおよびTypeScriptの場合はVitest)を使用し、ユニットスイートの速度を低下させないように別の設定またはプロジェクトで実行します。APIテストはPlaywrightの request フィクスチャ、または同じランナーを使用できます。
- ビルドされたアーティファクトを、ユーザーが実行するのと同じ方法で実行します。テスト専用のエントリポイントを介したソースコードではありません。
- 各テストに新しい一時ディレクトリを作業ディレクトリとして与え、
HOME、設定パス、キャッシュをそこに向けます。これにより、テストが開発者の実際のファイルを読み取ったり変更したりするのを防ぎます。 - 終了コード、stdout、stderr、および作成されたファイルを個別にアサーションします。「例外がスローされなかった」ことだけをチェックするテストは、間違った出力を見逃します。
- 出力を比較する前に、実行間で変化するもの(タイムスタンプ、一時パス、所要時間)を取り除くか固定します。
テストが失敗した場合
何かを変更する前に、証拠を確認します。
- アーティファクトを取得します。 失敗時にトレース、スクリーンショット、ビデオをオンにします(Playwright:
trace: 'on-first-retry'、screenshot: 'only-on-failure')。CI実行からトレースを開きます(npx playwright show-trace <trace.zip>)。トレースには、各ステップのページ、ネットワーク、コンソールが表示されます。 - バグの所有者を判断します。 アプリが間違っている場合はアプリを修正します。テストはその役割を果たしました。テストが間違っている場合はテストを修正します。環境が間違っている場合(サービスダウン、ポート使用中)はセットアップを修正します。
- 再現します。 CIが使用するのと同じコマンド、ブラウザ、設定(ヘッドレスモードとワーカー数を含む)で再現します。
- フレーキーなテストの場合、レースコンディションを見つけます。 一般的な原因は、固定ウェイト、ページ準備完了前のアクション、別のテストとのデータ共有、要素を覆うアニメーションやトーストです。その原因を修正し、修正完了と判断する前にテストを連続で複数回実行します(
npx playwright test <file> --repeat-each=20 --retries=0)。
CIのリトライはパイプラインをグリーンに保つことができますが、リトライでのみ成功したテストは依然として失敗しています。レポートで「フレーキー」と表示されたものは、ステータスではなく修正すべきバグとして扱ってください。ユーザーの同意なしにグリーン化のためにテストをスキップ、削除、隔離したり、アサーションを緩めてパスさせることは決してしないでください。
チェックリスト
E2Eテストを引き渡す前、またはレビューする際に:
- そのジャーニーはE2Eテストを必要とします。ユニットテストでチェックできるケースはユニットテストにします。
- ロケーターはrole、ラベル、テキスト、またはテストIDを使用します。CSSチェーンやXPathはありません。
- 固定ウェイト、強制アクション、グローバルタイムアウトの延長はありません。
- 各テストは独自のユニークなデータを作成し、単独でかつ並行して実行できます。
- サードパーティサービスのみがスタブ化されています。
- 失敗時にはCIでトレースまたはスクリーンショットが残ります。
- 新規または修正されたテストは、リトライをオフにして連続で複数回成功しました。