テストダブル
スタブ・モック・スパイ・フェイク・ダミーの違いと、テスト対象の境界を適切に分離するための使い分け
テストダブルはテスト実行時に本物の依存(DB・外部API・メール送信等)を置き換える代替オブジェクトの総称だ。Gerard Meszarosが『xUnit Test Patterns』で定義した5種類の分類が標準的な語彙として使われる。これらを混同するとテストの意図が曖昧になり、過剰なモックがテストの価値を下げるアンチパターンへと繋がる。
5種類の定義は以下の通りだ。Dummyは引数として渡されるが実際には使われないオブジェクト。Stubは呼び出しに対して固定の値を返すが、呼び出しを検証しない。Spyは実際の実装を持ちつつ呼び出し情報を記録し、後から検証できる。Mockは期待される呼び出しをあらかじめ設定し、テスト終了時に期待通りに呼ばれたかを検証する。Fakeは実際に動作する簡略実装で、In-memoryなDBやFakeメール送信がその代表だ。
依存性注入(DI)はテスタビリティと直結する。ハードコードされたnewによる依存生成はテストダブルへの置き換えを困難にする。コンストラクタインジェクションやファクトリ関数による注入を設計に組み込むことで、テスト時に柔軟に置き換えられる。外部HTTPリクエストのモックにはmsw(Mock Service Worker)が有効で、ネットワーク層をインターセプトするため実装コードを変更せずに使える。
コードレビューで着目するポイント
- MockとStubの使い分けが適切か(「呼ばれたか」を検証したいならMock、戻り値を制御したいならStub)
- Mockが実装の内部呼び出し順序や詳細を検証しすぎていないか(実装詳細のテスト)
- FakeオブジェクトがIn-memoryでも現実的な振る舞いを持っているか(バグを隠さないか)
- テストダブルが本物の依存と異なる振る舞いをして偽陽性・偽陰性を生じさせていないか
- DIの設計によってテストダブルの注入が容易になっているか
- mswなどのHTTPインターセプトがAPIスキーマと同期されているか
典型的なアンチパターン
モック地獄: すべての依存をモックし、テストコードの半分以上がモック設定になる。「ロジックが存在する」ことをテストしているだけで、実際の動作の保証にならない。
過剰なMock検証: expect(service.method).toHaveBeenCalledWith(...) の形で内部の呼び出し回数・引数を網羅的に検証する。実装を少し変えるたびにモックのアサーションが壊れ、メンテナンスコストが高い。
Fakeの不誠実な実装: FakeなDBが成功のみを返し、エラーケースや並行性の問題を再現しない。本物のDBで失敗するシナリオがFakeでは検出されず、統合テストで初めて問題が発覚する。
参考リソース
- Gerard Meszaros『xUnit Test Patterns: Refactoring Test Code』(Addison-Wesley、2007)
- msw https://mswjs.io — Service Worker / Node.jsのHTTPモックライブラリ
- Vitest の spy/mock API https://vitest.dev/api/mock
- Martin Fowler「Mocks Aren’t Stubs」 https://martinfowler.com/articles/mocksArentStubs.html
- Vladimir Khorikov『Unit Testing』第4章 — テストダブルの分類と使い分けの詳解
- 1. 📄アーキテクチャスタイル
- 2. 📄ドメインモデリング
- 3. 📄モジュール分割と依存管理
- 4. 📄データモデリング
- 5. 📄API設計
- 6. 📄整合性とトランザクション
- 7. 📄非同期処理(Queue/Event)
- 8. 📄キャッシング
- 9. 📄ユーザーリサーチ
- 10. 📄情報アーキテクチャ
- 11. 📄インタラクションデザイン
- 12. 📄UX原則とヒューリスティクス
- 13. 📄アクセシビリティ(UX観点)
- 14. 📄UXメトリクス
- 15. 📄スケーラビリティ
- 16. 📄可用性とレジリエンス
- 17. 📄オブザーバビリティ
- 18. 📄環境・インフラ設計
- 19. 📄データマイグレーション
- 20. 📄セキュリティ設計原則
- 21. 📄データ保護とマルチテナント
- 22. 📄LLMセキュリティ
- 23. 📄ビジュアルデザイン原則
- 24. 📄デザインシステム
- 25. 📄コンポーネント設計
- 26. 📄スタイリング
- 27. 📄状態管理
- 28. 📄フロントエンドパフォーマンス
- 29. 📄アクセシビリティ(実装観点)
- 30. 📄アニメーションとインタラクション
- 31. 📄設計原則
- 32. 📄デザインパターン(GoF)
- 33. 📄エンタープライズパターン
- 34. 📄クリーンコード
- 35. 📄リファクタリング
- 36. 📄型設計とコントラクト
- 37. 📄関数型プログラミング概念
- 38. 📄エラーハンドリング
- 39. 📄テスト戦略と哲学
- 40. 📄テスト種別(機能テスト)
- 41. 📄テスト種別(UI・ビジュアル)
- 42. 📄テスト種別(契約・境界)
- 43. 📄テスト種別(非機能テスト)
- 44. 📄テストダブル
- 45. 📄テスト設計技法
- 46. 📄並行処理・マルチスレッド
- 47. 📄パフォーマンス最適化
- 48. 📄ドキュメント管理
- 49. 📄バージョン管理と開発プロセス
- 50. 📄脅威モデリング
- 51. 📄通信保護(TLS)
- 52. 📄暗号化・機密情報管理
- 53. 📄認証・セッション管理
- 54. 📄認可・アクセス制御
- 55. 📄入力バリデーション
- 56. 📄インジェクション攻撃
- 57. 📄出力エンコーディング
- 58. 📄エラーハンドリング(セキュリティ観点)
- 59. 📄SSRF(サーバーサイドリクエストフォージェリ)
- 60. 📄依存関係管理
- 61. 📄設計・実装 参考書籍
- 62. 📄セキュリティ参考資料
- 63. 📄正規化(データベース正規化)