概要
テスト戦略とは、どの検証をどの層とどのツールで行うかという一連の決定です。本ページでは、フロントエンドテストの層モデルと各層の責務、そして新しい検証を正しい層に置くための判断基準を解説します。 フロントエンドのコードには、不具合の出方が異なる関心事が混在しています。描画のロジック、ユーザーの操作、見た目、バックエンドとの通信です。これらすべての不具合を妥当なコストで検出できる単一のテストはないため、フロントエンドのテストスイートは責務の異なる複数の層を組み合わせて作ります。 層に共通する原則は1つです。すべての検証を、その失敗を検出できる最も低い層で行います。低い層ほど速く実行できて原因を正確に指し示し、高い層はシステムの広い範囲を動かしたときにしか現れない失敗のために残します。 本ページで扱うのは、それぞれの検証をどの層に置くかです。個々のテストを信頼できる状態(通ることも失敗することも信頼できる状態)に保つ方法はテストコードを参照してください。Testing Trophy
Testing Trophyは、Kent C. Dodds氏が提唱したフロントエンドテストの層モデルです。テストをStatic・Unit・Integration・E2Eの4層に整理します。Static
型チェックとリントによる静的な解析です。コードを実行せずに、型の不一致や存在しない参照といった種類の欠陥をまとめて排除します。
Unit
単一の関数やモジュールを分離して検証します。速く正確で、入力パターンの多い純粋なロジックに向きます。
Integration
バックエンドとの境界をモックし、描画とユーザー操作を通してコンポーネントを検証します。トロフィーで最も広い層です。
E2E
デプロイされた環境に対して実際のブラウザを操作し、システム全体を通して主要な動線が完了することを検証します。
各層では、コストと信頼度がトレードオフです。高い層ほどユーザーの体験に近い形でシステムを動かすため、1本のテストが通ったときに保証できる範囲は広くなります。その一方で、実行は遅く、準備は多く、失敗は原因から遠い場所で報告されます。
低い層は速く、安く、正確です。たとえば型チェックが失敗すれば、問題のある行がそのまま示されます。その代わり、個々のチェックは分離された小さな範囲しか見ていないため、部品同士を組み合わせたときに動くことは証明できません。
トロフィーの形は、フロントエンドでのこのバランスの取り方を表しています。Integration層のテストが最も多いのは、コンポーネントテストが描画、状態、操作をまとめて検証でき、しかもデプロイされた環境なしにプルリクエストのパイプラインで速く実行できるためです。
各層で使うツールは少数に決まっています。
低い層はプッシュのたびにプルリクエストのパイプラインで実行し、E2Eテストスイートはデプロイ後にリリースの確認として実行します。パイプラインの設計はCI/CDを、E2Eをデプロイ後に実行する理由はE2Eテストを参照してください。
Static Test
Static Testは、コードを実行せずに行う静的な解析で、型チェックとリンターを指します。一度設定すればコードベース全体を毎回検証でき、検証1件ごとの追加コストがかからないため、トロフィーの最下層になります。 型チェックは、不正な状態を検出するのではなく排除するものです。型システムが拒否する呼び出しはコンパイルを通らないため、その値を1つずつ試す実行時テストは不要になります。不正な入力のテストを書く前に、型でその入力を不可能にできないかを確認します。TypeScript
Unit Test
Unit Testは、単一の関数やモジュールを分離して実行し、入力に対する出力を検証します。日付範囲の正規化や金額の計算のように、入力パターンの多い純粋なロジックが対象で、Vitestで書きます。Vitest
Integration Test
Integration Testは、コンポーネントとフックを組み合わせ、ユーザーの視点に近い形で検証します。バックエンドとの境界はモックし、描画と操作の結果を画面から観測します。 この層の検証は2種類です。振る舞いの検証では、Testing Libraryでコンポーネントを描画し、クリックや入力を再現して画面の変化を確かめます。バックエンドの応答はテストプロバイダーやテストツールで差し替え、実際に近いデータを与えたときの表示まで検証します。見た目の検証では、Storybookのストーリーを描画し、ビジュアルリグレッションテストで意図しない差分を検出します。Vitest + Testing Library
UnitとIntegrationは同じテストランナー(Vitest)で書くため、実際には区別されずに書かれることも多くあります。本ページでは、描画や操作を伴うかどうかで2つを区別します。
E2E Test
E2E Testは、デプロイされた環境に実際のブラウザでアクセスし、ログインから結果の表示までの主要な動線がシステム全体を通して完了することを検証します。ツールはPlaywrightです。Playwright
層の使い分け
検証を追加するときは、層を下から順に確かめ、その失敗を検出できる最初の層に置きます。判断の問いは次の4つです。- 型かリントのルールで保証できるなら、Staticに置きます。
- 単一の関数の入出力として検証できるなら、Unitに置きます。
- バックエンドをモックしたうえで、画面の描画や操作から観測できるなら、Integrationに置きます。
- どれにも当てはまらず、システム全体を動かして初めて検証できるなら、E2Eに置きます。
同じ失敗を2つの層で検出できるときは、低い方を選びます。低い層のテストはデプロイ後を待たずにプッシュのたびに実行され、失敗が症状ではなく原因を指し示すためです。
この規則は、E2Eテストスイートを小さく保つことにもつながります。コンポーネントテストで検出できる失敗はE2Eに持ち込まず、E2Eにはシステム全体を通して検証する動線だけを残します。詳細はE2Eテストの原則を参照してください。
検証を低い層に置いても、その上の層が不要になるわけではありません。たとえば、バックエンドをモックしたコンポーネントテストでは、実際のAPIの仕様と食い違っても検出できません。各層には、その層でしか検出できない失敗が残ります。
関連ページ
以下のページで、E2E層の詳細と守るテストの共通原則を扱います。E2Eテスト
小さなE2Eテストスイートで主要な動線を守る原則と、Playwrightで安定させるパターン。
テストコード
守るテストの原則。通ることと失敗することの信頼性、境界だけをモックすること。
CI/CD
各層が実行されるパイプライン。コミット時の検査からデプロイ後のE2Eまで。