モックの物語 — Endo-TestingからMockitoまで
2000年、マッキノン・フリーマン・クレイグの論文が「モックオブジェクト」という概念を提案し、後に「ロンドン学派」と呼ばれるテスト思想を生んだ。GOOS、jMock、Mockito、Sinon.jsへと連なる系譜と、メザロスによるテストダブルの5分類、そしてオーバーモッキングの弊害までを辿る。
TDD(第4記事)の実践者たちは、ごく早い段階である壁にぶつかっていた。「対象のオブジェクトが、データベースやネットワーク、他のオブジェクトに依存している場合、単体テストはどう書けばよいのか」という問いである。本物のデータベースに接続すればテストは遅く不安定になり、本物の外部APIを呼べば料金や副作用が発生しかねない。かといって依存先を単に「何も返さない偽物」に置き換えるだけでは、対象オブジェクトが依存先を正しく使っているかまでは検証できない。この課題に、2000年、一つの論文が明確な答えを与えた。
Endo-Testing論文 — 2000年、XP2000
2000年、イタリア・サルデーニャ島で開催されたXP2000カンファレンスで、ティム・マッキノン(Tim Mackinnon)、スティーブ・フリーマン(Steve Freeman)、フィリップ・クレイグ(Philip Craig)の3人が “Endo-Testing: Unit Testing with Mock Objects” という論文を発表した。モックオブジェクトという概念を世に問うた、最初の体系的な発表とされる。
論文名の「Endo(内側)」は、モックオブジェクトがドメインコードの内側に注入され、そこから振る舞いを観察されることに由来する。主張はシンプルだが強力だった。
- 対象オブジェクトが依存する協調オブジェクト(コラボレータ)を、本物の代わりに偽物へ差し替える
- ただし単に固定値を返すだけでなく、「期待された呼び出しが行われたか」を検証する —— これが従来の「スタブ」との決定的な違いだった
- モックを使えば、外部依存を持つコードでも高速・決定的な単体テストが書ける
- 「テストしやすい設計」を追い求める過程で、依存関係が明示的にオブジェクトへ注入される設計スタイル(依存性注入)が自然に導かれる
この論文は単なるテスト技法の提案にとどまらず、「テストを書くこと自体が良い設計を駆動する」というTDDの思想を補強するものとして受け止められた。
ロンドン学派の誕生
論文の著者たちはその後もこの実践を発展させ、モックオブジェクトを積極的に使い、オブジェクト間の「協調」や「対話」を軸に設計を進めるスタイルを確立していく。これが後にロンドン学派(London school)、あるいはモック主義(mockist) と呼ばれるテストスタイルの起源である。
その特徴は次の通りである。
- テスト対象(SUT)が送信するメッセージ(メソッド呼び出し)そのものを検証する —— 振る舞い検証(behaviour verification)
- 「外側から内側へ(outside-in)」設計を進める —— まずシステムの外部インターフェースをテストし、それを通すために必要な協調オブジェクトをモックとして先に定義し、それから実装を埋めていく
- オブジェクト間の責務分担やインターフェース設計そのものを、TDDのフィードバックループに組み込む
GOOS — 育てながらテストする(2009)
ロンドン学派の思想を一冊に体系立てたのが、スティーブ・フリーマンとナット・プライス(Nat Pryce)による著書 『Growing Object-Oriented Software, Guided by Tests』(通称GOOS、2009年)である。
この本は、単一クラスの単体テストにとどまらず、システム全体を「外側(ユーザーから見える振る舞い)」からテストで駆動しながら育てていく手法を、オークション参加クライアントというGUIアプリケーションの開発例を通じて示した。広く知られるようになった概念に次のものがある。
- Walking Skeleton: エンドツーエンドで動く最小限の骨組みを最初に作り、そこに肉付けしていく
- “Tell, Don’t Ask” の徹底とオブジェクト間の協調設計
- モックは「まだ存在しない協調オブジェクトの仕様を先に書く」ための道具である、という位置づけ
テストダブルという語彙の確立
「モック」「スタブ」「フェイク」といった言葉は当初、開発者ごとにバラバラに使われていた。ジェラルド・メザロス(Gerard Meszaros)は2004年、パターン言語カンファレンスPLoP 2004で発表した論文の中で、これらを包括する呼び名として 「テストダブル(Test Double)」 という用語を初めて提案した。「ダブル」は映画撮影の「スタントダブル(影武者)」からの比喩であり、「本物の代わりを務める役者」というイメージを的確に捉えている。
この用語は2007年の著書 『xUnit Test Patterns: Refactoring Test Code』 で正式に体系化され、5種類の分類として業界標準の語彙になった。
| 種別 | 役割 | 検証の性質 |
|---|---|---|
| ダミー(Dummy) | 引数を埋めるためだけに渡され、実際には使われない | なし |
| スタブ(Stub) | あらかじめ決められた値を返す | なし(状態検証の補助) |
| スパイ(Spy) | 呼び出しを記録し、後から「呼ばれたか」を確認できる | 状態検証寄り |
| モック(Mock) | 呼び出しの期待をあらかじめ設定し、実行中または実行後に検証する | 振る舞い検証 |
| フェイク(Fake) | 本物に近い簡易実装(インメモリDBなど) | 状態検証 |
この分類が重要なのは、「モック」という言葉が本来はテストダブルの一種類(振る舞い検証を行うもの)に過ぎないにもかかわらず、日常会話では「テストダブル全般」を指す拡大解釈された言葉として使われがちな点を整理したことにある。
jMock、EasyMock、Mockitoの系譜
モックオブジェクトの実践は、Javaのモックライブラリ群として具体化されていった。
| ライブラリ | 特徴 |
|---|---|
| jMock | Endo-Testing論文の著者らが開発に関与。厳密な期待値の事前宣言を重視する、ロンドン学派色の強いAPI |
| EasyMock | 「Record & Replay」という2段階のAPIスタイルでモックを生成。jMockより広く普及した |
| Mockito | 2008年、ロンドンの新聞社The Guardianのシステム開発の中でシュチェパン・ファーバー(Szczepan Faber)が中心となり開発。EasyMockのコードを土台に始まった |
Mockitoは「when(...).thenReturn(...)」のような読みやすいAPIと、「デフォルトでは緩やかな検証(呼び出し回数を厳密に指定しなくても動く)」を特徴とし、jMock/EasyMockの「事前に全ての期待値を宣言する」窮屈さを緩和した。この使いやすさから、Mockitoは2010年代以降、Java世界で事実上の標準的なモックライブラリの地位を確立した。
// Mockito: スタブとしての設定と、振る舞い検証の両方が書ける
PaymentGateway gateway = mock(PaymentGateway.class);
when(gateway.charge(1000)).thenReturn(true);
OrderService service = new OrderService(gateway);
service.checkout(order);
verify(gateway).charge(1000); // 呼び出されたことを事後的に検証
対照的に、jMock系のAPIは「呼び出しが起きる前に期待値を宣言する」スタイルを取る。
// jMock: 期待値を先に宣言してから実行する
context.checking(new Expectations() {{
oneOf(gateway).charge(1000); will(returnValue(true));
}});
service.checkout(order); // ここで期待に反する呼び出しがあれば即座に失敗
この違いは些細に見えて、「テストを読んだときに何を検証しているか分かりやすいか」という設計哲学の違いを反映している。
JavaScript世界のテストダブル
JavaScript世界ではSinon.jsが同様の役割を担い、スパイ・スタブ・モック・フェイクタイマーを統一的なAPIで提供するライブラリとして広く使われてきた。
// Sinon.js: スタブとスパイを組み合わせた例
const stub = sinon.stub(gateway, "charge").returns(true);
service.checkout(order);
assert(stub.calledWith(1000)); // スパイ的に呼び出し内容を確認
近年のJavaScriptエコシステムでは、JestやVitestが独自のモック機構(jest.fn()/vi.fn())を標準搭載しており、単独のモックライブラリを導入しなくてもテストダブルを扱えるようになっている。これもこの分野が十分に成熟し、言語・フレームワーク標準機能へ組み込まれる段階に達したことの表れといえる。
mockist vs classicist再訪
TDDの実践には大きく2つの流派がある。
- ロンドン学派(mockist): 協調オブジェクトは原則としてすべてモックに置き換え、SUTから送信されるメッセージ(振る舞い)を検証する。マッキノン・フリーマン・プライスらの系譜。
- デトロイト学派(classicist): Kent Beckに近い古典的立場で、可能な限り本物のオブジェクトを使い、最終的な状態(戻り値やオブジェクトの状態)を検証する(状態検証)。モックは「本物を使うのが著しく困難な場合」(外部API、時刻、ランダム性など)に限定して使う。
この対立は「テストは実装の詳細を知りすぎるべきか」という設計哲学の違いに根差しており、単なる好みの問題ではなく、リファクタリング耐性・テストの意図の明確さ・実行速度のトレードオフに直結する。
オーバーモッキングという代償
モックの普及とともに広く認識されるようになったのが、オーバーモッキング(over-mocking) の問題である。
- 協調オブジェクトをすべてモックに置き換えると、テストは「実装がどのメソッドをどの順序で呼ぶか」という実装の詳細を検証するものになりがちで、リファクタリングのたびにテストが壊れる
- モック同士の相互作用の設定が複雑化し、テストコード自体の可読性・保守性が落ちる
- 「モックが本物と同じ振る舞いをする」という前提が崩れると、テストは通るのに本番で壊れるという偽陰性が発生する
- 単体テストレベルで過度にモックを使うと、「テストが全て通っているのに結合すると動かない」という典型的な失敗パターンに陥る
こうした反省から、現代の実務では「モックは自分がオーナーでない境界(外部サービス・時刻・ファイルシステムなど)にのみ使い、自チームが所有するコラボレータは本物かフェイクを使う」という折衷的な指針が広く共有されている。この設計判断は、テストしやすい設計とインターフェース分割の問題(第12記事)とも密接に関わっている。
この記事から次の記事へ
モックは「特定の呼び出しが正しく行われたか」という具体的な振る舞いを検証する技法だった。次に見るのは、その正反対の発想である。個々の入出力例を書く代わりに、「どんな入力に対しても成り立つべき性質」を宣言し、大量のランダムな入力でツールに検証させるプロパティベーステスト——2000年にHaskellの世界で生まれたQuickCheckから話を始める。
参考文献
- Tim Mackinnon, Steve Freeman, Philip Craig, “Endo-Testing: Unit Testing with Mock Objects”(XP2000)
- Steve Freeman, Nat Pryce, Growing Object-Oriented Software, Guided by Tests(2009年)
- Gerard Meszaros, xUnit Test Patterns: Refactoring Test Code(2007年)
- Mockito公式ドキュメント(site.mockito.org)
- Sinon.js公式ドキュメント(sinonjs.org)