概要
E2E(End to End)テストは、実際のブラウザからアプリケーションを操作し、フロントエンド・バックエンド・データベースを通したシステム全体でユーザーの動線が完了することを検証するテストです。 E2Eは、ユーザーが体験するのと同じ形でシステムを検証できる唯一のテスト層ですが、実行と維持のコストは最も高くつきます。そのため、主要な動線を覆う小さなスイートを、失敗が常に意味を持つ信頼性で維持します。 本ページでは、E2Eテストスイートを信頼できる状態に保つ原則、Playwrightで安定したテストを書くためのパターン、そして外部サービスとの境界の橋渡し方を解説します。テストコード全般の原則はテストコードを参照してください。原則
すべてのケースではなく、主要な動線を覆う
E2Eテストはデプロイされた環境を必要とし、1本あたり数秒から数分かかり、ネットワークの状態・テストデータ・タイミングといったコード以外の要因でも失敗します。シナリオを追加するほどリリースのコストは上がるため、1本1本がそこに置かれる理由を持つ必要があります。 スイートは、壊れたら事業への影響が直ちに現れる動線に限定します。認証
ログインと会員登録です。ここが壊れると、すべてのユーザーが締め出されます。
収益に直結する動線
決済や注文の確定など、コンバージョンと利益に直結する操作です。
主要画面のスモーク
各画面が表示され、中心となる操作が完了することの確認です。
詳細は下位の層で検証する
実際のブラウザと実際のバックエンドを必要としない検証は、より下位の層に置きます。バリデーションの分岐・条件による表示の切り替え・コンポーネント内部のロジックはコンポーネントテストの方が速く正確に検証でき、E2Eは部品が正しくつながっていることだけを確認すれば足ります。 新しいシナリオをE2Eに置くかどうかは、1つの問いで判断します。その壊れ方は、システム全体を動かして初めて見えるものかどうかです。コンポーネントテストで捕まえられるなら、コンポーネントテストを書き、E2Eテストスイートはそのままにします。E2Eはデプロイ後にリリースの確認として実行する
E2Eテストはデプロイされた環境を検証するものなので、プルリクエストのパイプラインではなく、デプロイの後に実行します。リリースのマージによってステージング環境や本番環境へデプロイされたあと、これからユーザーが使う環境で主要な動線が動くことをスイートが確認します。 実行するシナリオはステージング環境と本番環境で変えます。選び方は後述の運用のセクションで扱います。 確認が失敗したら、その結果をロールアウトの停止や切り戻しというリリースの判断につなげます。デプロイを戻しやすく保つ方法はリリースを参照してください。 この分担により、最も遅い層がプルリクエストのクリティカルパスから外れてフィードバックが速く保たれ、それでいてすべてのリリースがシステム全体の検証を受けます。この分担が収まるパイプラインの設計はCI/CDを参照してください。不安定なテストは即座に直すか隔離する
欠陥がないのに断続的に失敗するテストは、偽陽性の発生源です。チームは失敗の内容を読む代わりに再実行するようになり、やがて本物の破損まで再実行して見逃します。失敗したE2Eの実行にはリリースを止める力が保たれている必要があるため、リリースパイプラインにおいてこれは致命的です。 テストが不安定になったら、気づいたその日のうちに修正するか隔離します。隔離するには、test.fixme()でテストをリリースの確認から外し、修正を課題として起票します。原因を調査している間も、スイートの残りが信頼できる状態に保たれます。
Playwright
Playwrightでの実践パターン
最初の3つのパターンはE2Eが不安定になる主要な原因に対応し、あとの2つは画面と権限が増えてもスイートの維持コストを一定に保つためのものです。ユーザーが見つけるのと同じ方法で要素を特定する
CSSクラスやDOM構造は実装の詳細です。スタイリングやリファクタリングによって挙動と無関係に変わり、そのたびにテストが壊れます。これは偽陽性です。 要素は、ロール・アクセシブルネーム・ラベルという、ユーザーが知覚するもので特定します。Playwright
時間ではなく、状態を待つ
固定時間のスリープはシステムの所要時間の推測であり、その推測は両方向に外れます。短ければ、負荷がかかったときにシステムが正しいのにテストが失敗します。安全なほど長くすれば、すべての実行でスイートが遅くなります。 Playwrightのweb-first assertionは期待する状態が現れるまで自動でリトライするため、テストは必要な時間だけ待ちます。Playwright
テストごとに専用のデータを用意する
他のテストが残した状態を読むテストは、連鎖的に失敗します。単独でも、並列でも、異なる順序でも実行できず、1本の破損が依存するテストを道連れにします。各テストが必要なアカウントとデータを自分で用意し、自分が作ったものだけを検証します。Playwright
認証はセットアップで一度だけ行い、再利用する
すべてのテストが冒頭でUIからログインする構成は、遅いうえに失敗を1箇所に集中させます。ログイン画面が変わると、スイート内の全テストが一斉に失敗します。 Playwrightでは認証とシナリオを分離できます。setupプロジェクトが権限ごとに一度だけログインし、storageStateで認証済みの状態をファイルに保存し、各テストはログイン済みの状態から始まります。
Playwright
画面×権限のスモークはデータから生成する
原則で述べたスモーク確認、すなわち主要な画面すべてが各権限で表示されることの確認はマトリクスであり、1マスずつ手で書く方法では規模に耐えません。画面の一覧を、それぞれを閲覧できる権限とともにデータとして一度だけ定義し、ループでテストを生成します。Playwright
外部サービスとの連携
外部サービスのログイン・メール配信・2要素認証のように、動線がブラウザの外に出る箇所は、ブラウザテストだけでは操作できず、稼働中の外部サービスに依存すると実行が非決定的になります。 テスト環境ではこの境界を2つの仕組みで橋渡しします。バックエンドの先で外部サービスの代わりに応答するスタブAPIと、UIからは到達できない状態をテストから準備・観測するためのテスト支援APIです。 これはテストコードの原則、すなわち外部境界をモックし検証対象のロジックはモックしないことのE2E版です。E2Eではバックエンド全体が検証対象のロジックなので、固定する境界はその先、システムがチームの制御下にないサービスを呼び出す縁にあります。状態の準備と観測はテスト支援APIで行う
シナリオが必要とする状態には、UIからは作成も読み取りもできないものがあります。登録導線のための新しい組織、2要素認証のワンタイムパスワード、アプリケーションが送信したメールの本文などです。 これらはテスト環境のテスト支援エンドポイントとして公開します。テストデータの作成と後片付け・コードの発行・配信されたメールの読み取りを用意すれば、シナリオは手作業なしで境界を越えられます。 メールの到達のようにブラウザの外で起きる事象は、web-first assertionの守備範囲外です。そのため、状態を待つというパターンはここでは形を変えます。固定時間のスリープではなく、上限付きのポーリングで待ちます。Playwright
スタブとテスト支援APIを本番に置かない
テスト支援エンドポイントは設計上強力です。アカウントを作成し、認証コードを発行し、メールを読み取ります。だからこそ、ユーザーが到達できる場所にあってはなりません。 同じ隔離はシナリオにも適用します。これらのエンドポイントでデータを作る動線は、本番環境ではなくステージング環境に対して実行します。その制限の実装方法は、後述の運用のセクションで扱います。スタブと実サービスの乖離に注意する
スタブは外部サービスの契約の複製であり、複製は乖離します。提供元がレスポンスの形や認証フローを変えても、スタブは古い形で応答し続け、実際の連携は壊れているのにスイートは通ります。これは偽陰性です。 寛容すぎるスタブは、連携テストが防ぐべき失敗をちょうど隠してしまいます。 2つの防御を組み合わせます。まず、スタブは薄く保ちます。アプリケーションが実際に読むエンドポイントとフィールドだけを実装すれば、乖離しうる面積が減ります。 そして、実際の連携を通すシナリオを少数、外部サービスの実アカウントを安全に使えるステージング環境で実行し続けます。実連携のシナリオが乖離を捕まえ、スタブがスイートの残りを決定的に保ちます。リリースパイプラインでの運用
環境ごとに実行するシナリオを選ぶ
同じスイートをすべての環境でフル実行する必要はありません。データを作るシナリオや外部サービスを呼ぶシナリオはステージング環境に対して実行し、本番環境では読み取り中心のスモーク、すなわち画面が表示され副作用のない動線が完了することを確認します。 この制限はパイプラインの設定ではなくテスト自体に書き込みます。パイプラインが変わってもルールが生き残るためです。Playwright
失敗はtraceで調査する
デプロイされた環境での失敗は手元で再現しにくいため、証拠は実行時に採取します。Playwrightのtraceは、各操作をスクリーンショット・コンソール出力・ネットワーク活動とともに記録します。失敗した実行のtraceを保存するようパイプラインを設定すれば、失敗した実行は再現に苦しむ謎ではなく、見返せる記録になります。 長いシナリオをtest.stepで段階に分けておくことも、ここで効きます。traceがどの行ではなくどの段階で失敗したかを示すようになります。
リトライで通ったら失敗のシグナルとして扱う
自動リトライは、インフラの一時的な不調と持続的な破損を区別でき、デプロイされた環境に対しては有効にする価値があります。 ただし、リトライで通ったテストは成功ではありません。それは不安定なテストであり、修正リストに載せます。リトライ後の成功を成功として数えると、スイートの信頼を損なう不安定さそのものが隠れてしまいます。リリースを判定できる速さを保つ
スイートはすべてのリリースを守るため、その実行時間はリリースのクリティカルパスに載ります。テストごとに独立したデータを持たせて安全に並列実行し、スイートの規模を意識的に抑えます。新しいシナリオが来たら、既存のシナリオが同じ動線を覆っていないか、下位の層で検証できないかを先に確認します。関連ページ
テストコード
守るテストの原則。通ることと失敗することの信頼性、境界だけをモックすること。
CI/CD
各テスト層を実行するパイプラインと、E2Eの起点になる自動デプロイ。
リリース
デプロイとリリースの分離と、リリースの確認が守るもの。