テストダブル完全ガイド:Vitest + React Testing Libraryでの実践
Martin Fowlerのテストダブル分類を基に、Vitest + React Testing Libraryでの実装パターンを解説。vi.fn()、vi.spyOn()、vi.mock()の使い分けとベストプラクティスを紹介します。

「モックはどこまで使うべき?」その議論の裏には、単体テストの考え方が根本的に異なる2つの流派がある。Martin Fowlerの「Mocks Aren't Stubs」とKhorikovの『単体テストの考え方/使い方』を基に、ロンドン派とデトロイト派の歴史・思想・使い分けを事実ベースで整理します。
「モックを使いすぎるとテストが壊れやすくなる」
「でもモックなしだとテストが遅くなるし、セットアップが大変」
この論争は、開発者コミュニティで繰り返されてきました。Stack OverflowやRedditで検索すれば、同じような議論が何年も続いていることに気づくでしょう。
実はこの対立の背景には、テストの考え方そのものが異なる「2つの流派」が存在します。
それがロンドン派(London School / Mockist)とデトロイト派(Detroit School / Classicist)です。
両派は「単体テスト」という同じ言葉を使いながら、その定義が異なります。だから話が噛み合わない。これが、モック論争が終わらない根本的な理由です。
この記事では、Martin Fowlerの「Mocks Aren't Stubs」(2004年初版、2007年改訂)とVladimir Khorikovの『単体テストの考え方/使い方』(原著2020年)を主な参照元として、両派の歴史的背景・思想・トレードオフを事実ベースで整理します。
なお、テストで使う「モック」「スタブ」などの用語には正確な分類があります。これらの違いについては以下の記事で詳しく解説しています。
デトロイト派の源流は、Kent Beckにあります。
Beckは1996年にChrysler社の給与計算システム「C3(Chrysler Comprehensive Compensation System)プロジェクト」に参加し、エクストリーム・プログラミング(XP)の実践としてテスト駆動開発(TDD)を確立しました。C3プロジェクトの拠点がデトロイト(ミシガン州)だったことが、「デトロイト派」の名前の由来です(出典:Mocks Aren't Stubs - Martin Fowler)。XPの全体像(価値・プラクティスの体系)については以下の記事で詳しく解説しています。
2002年に出版された『テスト駆動開発』(原著:Test-Driven Development: By Example)で、BeckはTDDの基本的なアプローチを体系化しました。そのアプローチの特徴は以下の通りです。
Martin Fowlerは「Mocks Aren't Stubs」の中で、古典派のスタイルをこう説明しています。
The classical TDD style is to use real objects if possible and a double if it's awkward to use the real thing. (古典的なTDDスタイルは、可能な限り本物のオブジェクトを使い、本物を使うのが厄介な場合にだけダブルを使うものである。)
ロンドン派のルーツは、2000年代初頭のロンドンにあります。
2000年、Tim Mackinnon、Steve Freeman、Philip Craigの3人は、XP2000カンファレンスで「Endo-Testing: Unit Testing with Mock Objects」と題した論文を発表しました。これが「モックオブジェクト」という概念を世に広めた最初期の文献です。
彼らはロンドンのコンテクスチュアル広告会社Connextraで働いており、XPの実践の中でモックオブジェクトの手法を開発しました(出典:A Brief History of Mock Objects)。その後、この考え方はロンドンのExtreme Tuesday Club(XTC)(毎週火曜日に集まるXP実践者のコミュニティ)を通じて広まりました。
このアプローチは、Steve FreemanとNat Pryceによって2009年の著書『Growing Object-Oriented Software, Guided by Tests』(通称GOOS)で体系化されました。
ロンドン派の特徴は以下の通りです。
Martin Fowlerは「Mocks Aren't Stubs」の中で、この2つの呼び名について触れています。
「古典派(Classical / Classicist)」と「モック派(Mockist)」という呼び方も広く使われています。

両派の違いは、突き詰めると「単体テストにおける"単体"とは何か?」という定義の違いに帰着します。
Vladimir Khorikovは『単体テストの考え方/使い方』の中で、この違いを3つの観点から整理しています。
| ロンドン派 | デトロイト派 | |
|---|---|---|
| 単体の単位 | 1つのクラス | 1つの振る舞い(複数クラスにまたがることもある) |
ロンドン派にとって、「単体テスト」とは1つのクラスをテストすることです。そのクラスが依存する他のクラスはすべてモックに置換します。
デトロイト派にとって、「単体テスト」とは1つの振る舞い(ユースケース)をテストすることです。その振る舞いの実現に複数のクラスが関わっていても構いません。
| ロンドン派 | デトロイト派 | |
|---|---|---|
| 何を隔離するか | テスト対象を依存から隔離 | テストケース同士を隔離 |
ロンドン派は「テスト対象のクラスを、その依存から隔離する」ことを重視します。だからすべての依存をモックに置換します。
デトロイト派は「テストケース同士が互いに影響しないこと」を重視します。共有状態(データベースなど)を持つ依存はモックに置換しますが、それ以外の依存は本物を使います。
Khorikovは依存を「共有依存(shared dependency)」と「プライベート依存(private dependency)」に分類し、各派がどう扱うかを整理しています。
| 依存の種類 | 例 | ロンドン派 | デトロイト派 |
|---|---|---|---|
| 共有依存(テスト間で状態を共有) | データベース、ファイルシステム | モック | モック |
| プライベート依存(可変) | 内部で生成されるオブジェクト | モック | 本物を使用 |
| プライベート依存(不変) | 値オブジェクト、定数 | 本物を使用 | 本物を使用 |
最大の違いは「プライベートな可変依存」の扱いです。ロンドン派はこれもモックに置換しますが、デトロイト派は本物を使います。

Martin Fowlerが「Mocks Aren't Stubs」で指摘したもう1つの重要な違いが、検証方法です。
状態検証は、テスト対象の操作を実行した後、結果の状態を確認するアプローチです。
[準備] → [実行] → [結果を確認]// デトロイト派のスタイル(状態検証)
it('注文に商品を追加できる', () => {
const warehouse = new Warehouse();
warehouse.add('りんご', 50);
const order = new Order('りんご', 10);
order.fill(warehouse);
// 状態を検証:注文が満たされたか? 在庫は減ったか?
expect(order.isFilled()).toBe(true);
expect(warehouse.getInventory('りんご')).toBe(40);
});このテストは、OrderやWarehouseの内部実装がどう変わっても、振る舞いが正しければパスし続けます。
振る舞い検証は、テスト対象が依存オブジェクトに対して正しいメソッド呼び出しを行ったかを確認するアプローチです。
[準備] → [実行] → [正しく呼び出されたか確認]// ロンドン派のスタイル(振る舞い検証)
it('注文に商品を追加できる', () => {
const mockWarehouse = {
hasInventory: vi.fn().mockReturnValue(true),
remove: vi.fn(),
};
const order = new Order('りんご', 10);
order.fill(mockWarehouse);
// 振る舞いを検証:正しいメソッドが正しい引数で呼ばれたか?
expect(mockWarehouse.hasInventory).toHaveBeenCalledWith('りんご', 10);
expect(mockWarehouse.remove).toHaveBeenCalledWith('りんご', 10);
});このテストは、OrderがWarehouseとどのようにコミュニケーションするかを検証しています。
Fowlerは「Mocks Aren't Stubs」の中で、自身の立場を明言しています。
I've always been a old fashioned classic TDDer and thus far I don't see any reason to change. (私はずっと昔ながらの古典派TDDerであり、いまのところ変える理由は見当たらない。)
ただし、Fowlerはモック派のアプローチに価値がないとは言っていません。優れた開発者がモック派のスタイルで成功していることも認めた上で、自分の好みを述べています。

両派はTDD(テスト駆動開発)の進め方も異なります。
デトロイト派のTDDは、内側のドメインロジックから外側へ向かって構築していきます。
1. ドメインモデル(最も内側)のテストを書く
2. ドメインモデルを実装する
3. それを使うサービス層のテストを書く
4. サービス層を実装する(本物のドメインモデルを使う)
5. さらに外側(API層など)へ進むこのアプローチでは、すでに動く内側のコードを土台にして、外側を構築します。
ロンドン派のTDDは、外側のインターフェースから内側へ向かって構築していきます。Freeman/Pryceの『Growing Object-Oriented Software, Guided by Tests』で詳述されたアプローチです。
1. ユーザーに最も近い層(UIやAPI)のテストを書く
2. 内側の依存はモックにして、まず外側を実装する
3. モックにした依存の実装に進む
4. さらに内側へ向かって実装するこのアプローチでは、ユーザーの要求から出発して、必要なインターフェースを発見していきます。Freeman/Pryceはこれを「テストに導かれた設計」と呼んでいます。
| Inside-Out(デトロイト派) | Outside-In(ロンドン派) | |
|---|---|---|
| 出発点 | ドメインモデル | ユーザーインターフェース |
| 設計の発見 | 内側から自然に浮かび上がる | 外側からの要求で決まる |
| モックの役割 | 外部依存の代替 | 未実装の依存のプレースホルダー |
| 利点 | 過剰設計を避けやすい | ユーザー要求との整合性が高い |
| 注意点 | 外側の設計が後回しになりがち | モック定義が設計を制約することがある |
Fowlerの分析とKhorikovの整理を基に、両派のトレードオフを整理します。
1. テスト失敗時にバグの場所が特定しやすい
すべての依存をモックに置換しているため、テストが失敗すれば、原因はそのクラス自身のコードにあると断定できます。デトロイト派では、本物の依存を使うため、依存先のバグでテストが連鎖的に失敗することがあります。
2. テストのセットアップがシンプルになる場合がある
依存関係が深いオブジェクトでも、モックなら簡単にセットアップできます。
3. オブジェクト間のインターフェース設計を促進する
依存をモックに置換するために、必然的にインターフェースを意識した設計になります。Freeman/Pryceはこれを「ロールインターフェース」と呼び、ロンドン派の重要な利点としています。
1. 実装詳細への結合
モックは「どのメソッドが、どの引数で呼ばれるか」を検証します。これは内部実装への結合を意味し、リファクタリング時にテストが壊れやすくなります。
Khorikovはこの点を強く批判しています。彼は、モックで検証すべきなのは「外部から観測可能な振る舞い(外部APIへのリクエストなど)」に限られると主張します。内部のオブジェクト間のやり取りをモックで検証すると、テストがリファクタリングの妨げになると述べています。
2. 偽陽性のリスク
すべてをモックに置換すると、オブジェクト間の統合が実際に機能するかどうかは検証されません。テストは通るのに、本番環境では動かないという「偽陽性」のリスクがあります。
1. リファクタリング耐性が高い
本物のオブジェクトを使い、結果の状態を検証するため、内部実装を変更してもテストは壊れにくいです。これはテストの長期的な保守性を高めます。
2. 統合レベルのバグを検出できる
複数のオブジェクトが実際に連携するため、オブジェクト間のインターフェースの不整合などを検出できます。
3. テストコードが仕様書として機能しやすい
「何を実行したら、何が起きるか」を状態で検証するため、テストコードが振る舞いの仕様書として読みやすくなります。
1. テスト失敗時の原因特定に時間がかかる場合がある
本物の依存を使うため、依存先のバグでテストが連鎖的に失敗し、原因の特定に手間取ることがあります。
2. セットアップが複雑になる場合がある
依存関係が深い場合、テストのセットアップに多くのオブジェクト生成が必要になることがあります。
「どちらが正解か」という議論には、明確な答えがありません。Khorikovは古典派を基本として推奨しつつも、ロンドン派が有効な場面があることも認めています。ここでは、プロジェクトの特性に応じた選択基準を提示します。
多くの現場では、両派の考え方を状況に応じて組み合わせるのが現実的です。
| テスト対象 | 推奨アプローチ | 理由 |
|---|---|---|
| ドメインロジック | デトロイト派(状態検証) | リファクタリング耐性が重要 |
| 外部API・メール送信 | ロンドン派(モック+振る舞い検証) | 副作用の検証が必要 |
| UIコンポーネント | デトロイト派寄り(ユーザー視点で状態検証) | 実装詳細への依存を避ける |
| アーキテクチャ境界 | ロンドン派(インターフェース検証) | 契約の遵守を検証 |
筆者は、プロジェクトの成熟度に応じて流派を切り替えるアプローチを実践しています。
初期段階(ゼロからのスクラッチ開発)ではデトロイト派を採用します。開発初期はコードの変更が激しく、インターフェースも頻繁に変わります。この段階でロンドン派を使うと、インターフェース変更のたびにモック定義の更新が必要になり、テストのメンテナンスコストが跳ね上がります。デトロイト派であれば、本物のオブジェクトを使って結果の状態を検証するので、内部構造が変わってもテストは壊れにくいです。
コードが安定してきたフェーズではロンドン派を取り入れます。インターフェースが固まった段階でモックに切り替えると、テストの実行速度が上がり、テスト対象の責務も明確になります。安定したインターフェースに対するモック定義は変更頻度が低いため、メンテナンスコストも抑えられます。
この「デトロイト派から始めて、安定したらロンドン派を取り入れる」というアプローチは、両派のメリットをプロジェクトのライフサイクルに合わせて活用するものです。
テスト戦略全体の設計(単体テスト・結合テスト・E2Eテストの配分)については、以下の記事で詳しく解説しています。
ロンドン派とデトロイト派の違いは、つまるところ「単体テストにおける"単体"をどう定義するか」の違いです。
| ロンドン派(Mockist) | デトロイト派(Classicist) | |
|---|---|---|
| 単体の定義 | 1つのクラス | 1つの振る舞い |
| 隔離の対象 | テスト対象 ↔ 依存 | テストケース ↔ テストケース |
| 検証方法 | 振る舞い検証 | 状態検証 |
| TDDスタイル | Outside-In | Inside-Out |
| 代表的な文献 | Freeman/Pryce『GOOS』 | Beck『テスト駆動開発』 |
「どちらを選ぶべきか」よりも重要なのは、自分のプロジェクトでなぜそのアプローチを採るのかを説明できることです。モックを多用するなら、それが意図的な設計判断であるべきです。本物のオブジェクトを使うなら、リファクタリング耐性を重視しているという判断があるべきです。
どちらの流派にも、優れた実践者と、数十年の実績があります。大事なのは教条的にならず、プロジェクトの特性に応じて適切な手法を選ぶことです。