モックとは?
モックとは、テストにおいて実際の依存関係の代わりとなり、コードがそれとどのようにインタラクションするかを検証する偽物のオブジェクトです。
テスト対象のコードが、決済ゲートウェイやメールサービスのように遅い、外部にある、または制御が難しい何かに依存している場合、それをモックに置き換えます。モックは定型的な応答を返すだけでなく、どのように呼び出されたかを記録してアサーションを行うため、実際のシステムに触れることなく振る舞いを検証できます。
モックの役割
モックは実際の協力オブジェクトをプログラム可能な代役に置き換えます。何を返すかを設定し、コードが正しい方法で、正しい引数で、正しい回数だけそれを呼び出したことをアサートします。このインタラクションの検証こそが、モックを単なるスタブと区別するものです。
実践的な例
通知処理をテストする際、メールクライアントをモックにし、実際にメールを送信することなく、send が期待した宛先で一度だけ呼び出されたことをアサートします。
const email = { send: jest.fn() };
notifyUser(email, "ada@example.com");
expect(email.send).toHaveBeenCalledWith("ada@example.com");テストダブルの中でのモック
- スタブ: 定型的な値を返し、インタラクションのチェックは行わない。
- モック: 値を返し、どのように呼び出されたかを検証する。
- スパイ: 実際のオブジェクトをラップし、それへの呼び出しを記録する。
- フェイク: 動作するが簡略化された実装。
モックが役立つときと害になるとき
モックは外部依存を取り除くことで、ユニットテストを高速かつ決定的にします。しかし過剰なモックは、実際の統合が壊れているのにパスしてしまうテストを生み出す可能性があります。これは、現実ではなく自分の想定に対してアサートしているためです。モック中心のユニットテストと、いくつかの実際の統合テストのバランスを取りましょう。
モックとCIの信頼性
モックはネットワークとI/Oを取り除くため、適切にモックされたユニットテストは高速で、CIでほとんどフレーキーになりません。フレーキーさは、一時的な失敗が起きやすい、モックされていない統合レイヤーやE2Eレイヤーに存在する傾向があります。Latchkey はそうした一時的なフレークを自動でリトライするため、実際の依存関係の不具合が、本来グリーンであるはずのビルドを失敗させることはありません。
重要なポイント
- モックは実際の依存関係を置き換え、それがどのように呼び出されたかを検証します。
- インタラクションの検証こそが、モックをスタブと区別するものです。
- 過剰なモックは、実際の統合が壊れているのにテストがグリーンになるリスクを招きます。