コンテンツにスキップ

【比較】単体テスト・結合テスト・E2Eテストの違い

概要

「単体テスト(Unit Test)」「結合テスト(Integration Test)」「E2Eテスト(End-to-End Test)」は、自動テストを「どこまでの範囲を、どれだけ隔離して検証するか」で区別したものです。単体テストは1つの関数やクラスといった狭い範囲のロジックを、依存先を切り離して高速に検証します。結合テストは複数のモジュールや、データベース・外部APIといった実依存との境界(シリアライズ/通信)を検証します。E2Eテストはユーザーやクライアントから見た一連の流れを、システム全体を実際に動かして検証します。

この3つは「テストピラミッド」という指針で語られることが多く、Mike Cohn が著書『Succeeding with Agile』で提唱し、Martin Fowler らが整理しました。本記事では、Fowler の "The Practical Test Pyramid" と "Test Double" を一次情報として、範囲・速度・隔離・フィードバックの観点で正確に区別します。

例えるなら:自動車の検査

単体テストは「ボルト1本のトルクを測る」検査、結合テストは「ブレーキとペダルを繋いで踏み心地を確認する」検査、E2Eテストは「完成車をテストコースで実際に走らせる」検査に近いものです。完成車の試走は最も「本当に動くか」の確信を与えますが、遅く・高コストで、不具合が出ても原因箇所の特定が難しい。だからボルト単位の検査を大量に、試走を少数に、というバランスになります。

詳細比較

項目 単体テスト (Unit) 結合テスト (Integration) E2Eテスト (End-to-End)
検証範囲 1関数〜1クラスなど狭い単位のロジック 複数モジュール間/DB・FS・外部APIとの境界 UI/API を入口にしたシステム全体の一連の流れ
依存の扱い テストダブル(モック/スタブ等)で原則隔離(solitary)。協調する依存を実物で使う流儀(sociable)もある DB やメッセージキュー等を実物で動かし、シリアライズ/デシリアライズの境界を検証 本物のサービス群を起動し、極力モックしない
速度 非常に高速(ミリ秒オーダー、数千件を秒で実行可) 中程度(実依存の起動・I/Oで遅くなる) 低速(ブラウザ起動・ネットワーク・複数サービス連携)
不安定さ(flaky) 低い(決定的になりやすい) 中(外部状態・タイミング依存) 高い。Fowler も「予期できない理由で頻繁に失敗しがち(notoriously flaky)」と指摘
失敗時の原因特定 容易(壊れた箇所がほぼ自明) やや困難(どの境界かを切り分け要) 困難(広い範囲のどこが原因か追跡が必要)
得られる確信 ロジックが正しいという確信 モジュール/外部システムと正しく繋がる確信 「ソフトが実際に動く」最大の確信
保守コスト 低い 中 高い(壊れやすく書き換え頻度も高い)
ピラミッド上の量 多数(土台) ある程度 ごく少数(頂点)

テストピラミッドの核となる主張は2つです。「粒度の異なるテストを書くこと」と「高レベル(広範囲)になるほどテストの数は少なくすべきこと」。Fowler の言葉では "Write lots of small and fast unit tests. Write some more coarse-grained tests and very few high-level tests that test your application from end to end." とまとめられます。

「単体テスト」の単位は言語により幅があり、オブジェクト指向言語では1メソッド〜1クラス程度を指します。依存を全てスタブ化して完全隔離する流儀を solitary(孤立的)、遅い/副作用のある協調オブジェクトだけ差し替えて実物も使う流儀を sociable(社交的)と呼びます。どちらを取るかは流派の問題で、どちらかだけが正解ではありません。

よくある誤解

  • 「カバレッジ100%ならバグはない」は誤り。 コードカバレッジは「テスト実行時に通過した行/分岐の割合」を測るだけで、その結果が正しいかを検証したかは保証しません。表明(アサーション)が弱ければ全行通過でもバグは残ります。境界値・異常系・並行性・仕様誤りはカバレッジ数値に現れません。100%は「最低限ここは通した」であって「バグゼロ」ではありません。

  • 「E2Eを厚くすれば単体テストはいらない」は誤り(アイスクリームコーン・アンチパターン)。 ピラミッドを上下反転させ、遅く壊れやすいE2Eに依存し単体テストが薄い状態は典型的なアンチパターンです。E2Eは確信は大きい一方で遅く・flaky で・失敗時の原因特定が難しいため、これだけに頼るとスイートが信頼されなくなります。土台の単体テストを多く、E2Eを少なく保つのが指針です。

  • 「モックとスタブは同じ」は誤り。 Gerard Meszaros の分類(Fowler が紹介)では、スタブ(Stub)は「呼び出しに対して用意した固定の答えを返す」もので状態検証に使い、モック(Mock)は「期待する呼び出しの仕様を事前にプログラムされ、検証時に期待通り呼ばれたかをチェックする」振る舞い検証用です。さらに Fake は「動く実装を持つが本番には不適(例:インメモリDB)」と、別物として区別されます。「テストダブル」が総称で、その下位にこれらがあります。

実務での選び分け

  • ロジックの正しさを素早く確かめたい/TDDの内側ループ → 単体テスト。高速で原因特定が容易なので最も多く書く。
  • DB・外部API・メッセージング等との接続部や境界(シリアライズ)を検証したい → 結合テスト。実依存をコンテナ等で起動して確認する。
  • 「ユーザー操作で重要シナリオが端から端まで通るか」のリグレッション防止 → E2Eテストを主要シナリオに絞って少数だけ用意する。網羅はしない。
  • テストが遅い/不安定(flaky)で信頼されない → ピラミッド形に近づける。高レベルテストを減らし、同じ確認を下位レベルへ「押し下げ」られないか検討する。

ひとことまとめ

単体=隔離した狭いロジックを大量・高速に、結合=実依存との境界を中量で、E2E=システム全体の主要シナリオをごく少数で、というピラミッド型の配分が、確信・速度・保守コストのバランスを最適化します。

出典・参考