ユニットテストで必須!モックとスタブの決定的な違いを徹底解説
テストの落とし穴?モックとスタブ、決定的な違いを理解する
「ユニットテストを書いているのに、なぜかこの概念が掴みきれない…」
ソフトウェア開発において、外部依存性を持つコンポーネント(データベース接続、API呼び出しなど)を扱う際に、「モック (Mock)」や「スタブ (Stub)」という言葉を耳にすることは非常に多いです。
しかし、この二つの用語は日常的に混同されがちです。どちらもテストの分離(Isolation)を目的として使用されますが、実は彼らが担う役割と検証する振る舞いには決定的な違いがあるのです。
この記事では、それぞれの定義から、「何の違いで区別すれば良いのか?」という核心部分までを、具体的に解説します。
そもそもなぜこれらが必要なのか?依存性の問題
ユニットテストの目的は、「今テストしている関数やクラスが、純粋に正しいロジックを持っているか」を確認することです。しかし、実際のコードには必ず「外部への依存性」があります。例えば、「ユーザーデータを取得する機能A」がある場合、このAは必ず「データベース層B」に頼ります。
もしテストのたびに実際にDB接続を行うとどうなるでしょうか?
- テスト時間が長すぎる(実行ごとにネットワークやディスクI/Oが発生するため)。
- テストが不安定になる(DBの状態によって結果が変わる、など)。
そこで登場するのが、外部依存の代わりに「偽物」を差し込む技術であり、それがスタブやモックなのです。
1.【Stub】状態を提供する「データ代わり」
スタブとは? (The Data Provider)
スタブは非常に単純です。テストが動作するために必要な、あらかじめ用意された「戻り値(State)」だけを提供します。外部依存のシミュレーターだと考えると分かりやすいでしょう。
役割と思考法
スタブの目的は、あくまでも「正しいデータ」を差し入れることで、テスト対象のコードが「そのデータを受け取ったときにどう振る舞うか」を確認することです。
スタブ自身は、「今呼ばれたかどうか」「何回呼ばれたか」といった呼び出し履歴については全く関知しません。
例えるなら?
レストランで「カレーライスが食べたい」とき、メニューに書かれた「(味のサンプル)レモンソースをかけたカレー写真」を受け取るようなものです。実際に調理されてはいませんが、それを見るだけで十分テストはできます。単なる入力データです。
2.【Mock】振る舞いを検証する「監視役」
モックとは? (The Behavior Validator)
モックは、スタブ以上の機能を持っています。それは、「呼び出し(Interaction)」や「挙動(Behavior)」を記録し、テスト後にその履歴を検証できる点です。
役割と思考法
モックの目的は、「このメソッドが、想定通りに呼ばれたかどうか」「何回呼ばれたか」「どの引数で呼ばれるべきだったか」といった「相互作用(Interaction)」自体をテストし、検証することにあります。
単なるデータの提供だけでなく、外部との契約通りのやり取りが行われたかをチェックする、非常に厳密な監視役のようなものです。
例えるなら?
あなたが同僚Aの仕事ぶりを確認したいとき、「メールを3回送ったか」「そのうち1回は必ず上司にCcを入れたか」という行動履歴自体が重要になります。モックはこの「行動履歴(振る舞い)」を記録・検証します。
核心的な違い:確認するものとは?
この二つの概念の違いを最も明確に理解するには、「何を確認しているのか?」という視点で捉えることが非常に有効です。
| 項目 | スタブ (Stub) | モック (Mock) |
|---|---|---|
| 主たる目的 | テストに必要な「状態(データ)」を提供する。 | テスト対象が外部に要求する「振る舞い」を検証する。 |
| 検証の焦点 | 返ってきた値や戻り値が正しいか (State)。 | メソッドが正しく呼ばれたかどうか、その回数、引数が正しいか (Behavior/Interaction)。 |
| 振る舞い方 | 単純なデータホルダー。呼び出し履歴は保持しない。 | 内部にロジックを持ち、呼び出しのカウントや記録を行う。 |
まとめ:使い分けのガイドライン
どのテストを使うべきか迷った際は、この質問を自分に投げかけてみてください。
- 「私は単に『正しいデータ』さえ渡してもらえればいい。呼び出し回数は気にしなくて良い。」→ スタブで十分
- 「私は、このメソッドが『必ず一度だけ呼ばれ、特定の引数を受け取っていること』を保証したい。」→ モックを使う必要がある
多くの場合、スタブはテストの前提条件(事前準備)を提供するために使われ、モックはそれ以上の「契約不履行」がないかを検証する、という使い分けがされています。この視点を持つことで、「概念的な混同」から抜け出すことができるはずです。
これらの知識をユニットテストに組み込み、堅牢で信頼性の高いコードベースを目指しましょう。
コメント
コメントを投稿