私はユニット テストの初心者ですが、「モック オブジェクト」という言葉をよく耳にします。わかりやすい言葉で、モック オブジェクトとは何か、ユニット テストを書くときに通常何に使用されるのかを説明していただけますか?
ベストアンサー1
ユニット テストは初めてで、モック オブジェクトを「一般の人にもわかる言葉」で説明してほしいとのことなので、一般の人にもわかる例を試してみます。
ユニットテスト
このシステムのユニットテストを想像してください:
cook <- waiter <- customer
一般的に、次のような低レベルコンポーネントのテストを想像するのは簡単ですcook
。
cook <- test driver
テストドライバーは、さまざまな料理を注文し、料理人が各注文に対して正しい料理を返すかどうかを確認するだけです。
ウェイターのように、他のコンポーネントの動作を利用する中間コンポーネントをテストするのは困難です。初心者のテスターは、クック コンポーネントをテストしたのと同じ方法でウェイター コンポーネントをテストするかもしれません。
cook <- waiter <- test driver
テスト ドライバーはさまざまな料理を注文し、ウェイターが正しい料理を返すようにします。残念ながら、これはウェイター コンポーネントのこのテストが、調理コンポーネントの正しい動作に依存する可能性があることを意味します。調理コンポーネントに、非決定論的な動作 (メニューにシェフのサプライズが料理として含まれている)、多くの依存関係 (調理人はスタッフ全員がいないと調理しない)、または多くのリソース (一部の料理には高価な材料が必要であったり、調理に 1 時間かかる) などのテストに不向きな特性がある場合、この依存関係はさらに悪化します。
これはウェイターのテストなので、理想的には、コックではなくウェイターだけをテストします。具体的には、ウェイターが顧客の注文をコックに正しく伝え、コックの料理を顧客に正しく届けていることを確認します。
ユニットテストとは、ユニットを独立してテストすることを意味するので、より良いアプローチは、テスト対象のコンポーネント(ウェイター)を分離することです。ファウラーはテストダブル(ダミー、スタブ、フェイク、モック)を呼び出します。
-----------------------
| |
v |
test cook <- waiter <- test driver
ここでは、テスト クックはテスト ドライバーと「共謀」しています。理想的には、テスト対象のシステムは、テスト クックを簡単に置き換えることができるように設計されます (注入された) を使用して、プロダクション コードを変更せずに (つまり、ウェイター コードを変更せずに) ウェイターを操作できます。
モックオブジェクト
現在、テスト クック (テスト ダブル) はさまざまな方法で実装できます。
- 偽の料理人 - 冷凍食品と電子レンジを使って料理人のふりをする人。
- スタブクック - 何を注文しても必ずホットドッグを出してくれるホットドッグ屋、または
- 模擬コック - 台本に従っておとり捜査でコックのふりをする覆面警官。
見るフェイクとスタブとモックとダミーについての詳細は、Fowler の記事をご覧ください。ですが、今は模擬料理に焦点を当てましょう。
-----------------------
| |
v |
mock cook <- waiter <- test driver
ウェイター コンポーネントのユニット テストの大部分は、ウェイターがクック コンポーネントとどのようにやり取りするかに焦点を当てています。モック ベースのアプローチは、正しいやり取りを完全に指定し、問題が発生したときにそれを検出することに重点を置いています。
モック オブジェクトは、テスト中に何が起こるか (たとえば、どのメソッド呼び出しが呼び出されるかなど) を事前に認識しており、どのように反応するか (たとえば、どの戻り値を提供するか) も認識しています。モックは、実際に起こることと、起こるはずのことが異なるかどうかを示します。各テスト ケースで期待される動作を実行するために、カスタム モック オブジェクトを最初から作成することもできますが、モック フレームワークは、このような動作仕様をテスト ケースで直接、明確かつ簡単に指定できるように努めています。
模擬テストに関する会話は次のようになります。
テストドライバーに模擬料理人:ホットドッグの注文を予想し、それに応えてこのダミーのホットドッグを渡す
テストドライバー(顧客を装って)ウェイター:ホットドッグをお願いします
ウェイターに模擬料理人:ホットドッグを1つください
模擬料理人にウェイター:注文:ホットドッグ1個用意完了(ダミーのホットドッグをウェイターに渡す)
ウェイターにテストドライバー:ホットドッグはこちらです(テストドライバーにダミーのホットドッグを渡す)テストドライバー: テスト成功!
しかし、私たちのウェイターは新人なので、次のようなことが起こる可能性があります。
テストドライバーに模擬料理人:ホットドッグの注文を予想し、それに応えてこのダミーのホットドッグを渡す
テストドライバー(顧客を装って)ウェイター:ホットドッグをお願いします
ウェイターに模擬料理人:ハンバーガーを1つください
模擬料理人テストを停止します:ホットドッグの注文を期待するように言われました!テストドライバー問題をメモ: テスト失敗! - ウェイターが注文を変更しました
または
テストドライバーに模擬料理人:ホットドッグの注文を予想し、それに応えてこのダミーのホットドッグを渡す
テストドライバー(顧客を装って)ウェイター:ホットドッグをお願いします
ウェイターに模擬料理人:ホットドッグを1つください
模擬料理人にウェイター:注文:ホットドッグ1個用意完了(ダミーのホットドッグをウェイターに渡す)
ウェイターにテストドライバー:フライドポテトです(他の注文のフライドポテトをテストドライバーに渡す)テストドライバー予想外のフライドポテトに気づく:テスト失敗!ウェイターは間違った料理を返した
対照的なスタブベースの例がなければ、モック オブジェクトとスタブの違いを明確に理解するのは難しいかもしれませんが、この回答はすでに長すぎます :-)
また、これは非常に単純な例であり、モック フレームワークを使用すると、包括的なテストをサポートするために、コンポーネントの予想される動作をかなり高度に指定できることにも注意してください。詳細については、モック オブジェクトとモック フレームワークに関する資料が豊富にあります。