web-dev-qa-db-ja.com

モックオブジェクトの目的は何ですか?

ユニットテストは初めてであり、「モックオブジェクト」という言葉が頻繁に聞かれます。素人の言葉で、誰かがモックオブジェクトとは何か、そして単体テストを書くときにそれらが一般的に使用されるものを説明できますか?

155
agentbanks217

あなたはユニットテストが初めてであり、「素人の用語」でモックオブジェクトを要求したと言うので、素人の例を試してみましょう。

単体テスト

このシステムの単体テストを想像してください:

cook <- waiter <- customer

一般に、cookのような低レベルコンポーネントのテストは簡単に想像できます。

cook <- test driver

テストドライバーは、単に異なる料理を注文し、料理人が注文ごとに正しい料理を返すことを確認します。

他のコンポーネントの動作を利用するウェイターなどの中間コンポーネントをテストするのはより困難です。素朴なテスターは、クックコンポーネントのテストと同じ方法でウェイターコンポーネントをテストできます。

cook <- waiter <- test driver

テストドライバーはさまざまな料理を注文し、ウェイターが正しい料理を返すようにします。残念ながら、これは、ウェイターコンポーネントのこのテストがクックコンポーネントの正しい動作に依存する可能性があることを意味します。この依存関係は、非決定的な動作(メニューに料理人の驚きがメニューに含まれる)、多くの依存関係(料理人がスタッフ全員なしで調理しない)、またはリソース(一部の料理は高価な材料を必要とするか、調理に1時間かかります)。

これはウェイターテストであるため、理想的には、クックではなくウェイターのみをテストします。具体的には、ウェイターがお客様の注文を料理人に正しく伝え、料理人の食べ物をお客様に正しく配達するようにします。

ユニットテストはユニットを個別にテストすることを意味するため、より良いアプローチは、テスト対象のコンポーネント(ウェイター)を Fowlerがテストダブル(ダミー、スタブ、フェイク、モック)を呼び出す を使用して分離することです。

    -----------------------
   |                       |
   v                       |
test cook <- waiter <- test driver

ここでは、テストコックはテストドライバーと「インフー」です。理想的には、テスト対象のシステムは、テストコードを簡単に置き換えて( injected )、生産コードを変更せずに(たとえば、ウェイターコードを変更せずに)ウェイターと連携できるように設計されています.

モックオブジェクト

これで、テストクック(テストダブル)をさまざまな方法で実装できます。

  • 偽の料理人-冷凍ディナーと電子レンジを使用して料理人のふりをする人、
  • スタブクック-何を注文しても常にホットドッグを提供するホットドッグベンダー、または
  • モッククック-刺すような操作で料理をするふりをしているスクリプトに従った覆面警官。

偽物対スタブ対モック対ダミーの詳細については、ファウラーの記事 を参照してください。ただし、ここでは、モッククックに焦点を当てましょう。

    -----------------------
   |                       |
   v                       |
mock cook <- waiter <- test driver

ウェイターコンポーネントをテストするユニットの大部分は、ウェイターがクックコンポーネントと対話する方法に焦点を当てています。モックベースのアプローチは、正しい相互作用が何であるかを完全に特定し、それがいつうまくいかないかを検出することに焦点を合わせています。

モックオブジェクトは、テスト中に何が起きるか(たとえば、どのメソッド呼び出しが呼び出されるかなど)を事前に知っており、モックオブジェクトは、どのように反応するか(たとえば、どの戻り値を提供するか)を知っています。モックは、実際に起こることと起こるはずのこととが異なるかどうかを示します。カスタムのモックオブジェクトを各テストケースに対してゼロから作成して、そのテストケースに期待される動作を実行することができますが、モックフレームワークは、そのような動作仕様をテストケースで明確かつ簡単に直接示すことを可能にします。

モックベースのテストを取り巻く会話は次のようになります。

test drivertomock cookホットドッグを期待注文して、応答でこのダミーのホットドッグを彼に渡す

test driver(顧客としてポーズ)towaiterホットドッグをお願いします
waitertomock cookホットドッグを1つください
mock cooktowaiter注文:ホットドッグ1枚準備完了(ダミーホットドッグをウェイターに提供)
waitertotest driverこちらがホットドッグです(テストドライバーにダミーのホットドッグを提供します)

テストドライバー:テストは成功しました!

しかし、ウェイターは新しいので、これが起こる可能性があります:

test drivertomock cookホットドッグを期待注文して、応答でこのダミーのホットドッグを彼に渡す

test driver(顧客としてポーズ)towaiterホットドッグをお願いします
waitertomock cookハンバーガー1個ください
mock cookはテストを停止します:ホットドッグの注文を期待するように言われました!

テストドライバーは問題に注意します:テストが失敗しました! -ウェイターが注文を変更しました

または

test drivertomock cookホットドッグを期待注文して、応答でこのダミーのホットドッグを彼に渡す

test driver(顧客としてポーズ)towaiterホットドッグをお願いします
waitertomock cookホットドッグを1つください
mock cooktowaiter注文:ホットドッグ1枚準備完了(ダミーホットドッグをウェイターに提供)
waitertotest driverフライドポテトはこちらです(他の注文のフレンチフライを運転手に提供します)

テストドライバーは、予期しないフレンチフライに注意します:テストが失敗しました!ウェイターが間違った料理を返した

これとは対照的なスタブベースの例がなければ、モックオブジェクトとスタブの違いをはっきりと見るのは難しいかもしれませんが、この答えはすでに長すぎます:-)

また、これは非常に単純な例であり、モックフレームワークでは、コンポーネントから予想される動作のかなり洗練された仕様により、包括的なテストをサポートできることに注意してください。詳細については、モックオブジェクトとモックフレームワークに関する資料がたくさんあります。

332
Bert F

モックオブジェクトは、実際のオブジェクトを置き換えるオブジェクトです。オブジェクト指向プログラミングでは、モックオブジェクトは、制御された方法で実際のオブジェクトの動作を模倣するシミュレートされたオブジェクトです。

通常、コンピュータープログラマーはモックオブジェクトを作成して、他のオブジェクトの動作をテストします。これは、自動車の設計者が衝突テストダミーを使用して、車両の衝撃における人間の動的な動作をシミュレートするのとほぼ同じです。

http://en.wikipedia.org/wiki/Mock_object

モックオブジェクトを使用すると、データベースなどの扱いにくい大規模なリソースを負担することなく、テストシナリオを設定できます。テストのためにデータベースを呼び出す代わりに、単体テストでモックオブジェクトを使用してデータベースをシミュレートできます。これにより、クラス内の1つのメソッドをテストするだけで、実際のデータベースをセットアップおよび破棄する負担から解放されます。

「モック」という言葉は、「スタブ」と同じ意味で誤って使用されることがあります。 2つの単語の違いは ここで説明します 基本的に、モックは、テスト対象のオブジェクト/メソッドの適切な動作に対する期待(つまり、「アサーション」)も含むスタブオブジェクトです。

例えば:

class OrderInteractionTester...
  public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    Mock warehouse = mock(Warehouse.class);
    Mock mailer = mock(MailService.class);
    order.setMailer((MailService) mailer.proxy());

    mailer.expects(once()).method("send");
    warehouse.expects(once()).method("hasInventory")
      .withAnyArguments()
      .will(returnValue(false));

    order.fill((Warehouse) warehouse.proxy());
  }
}

warehouseおよびmailerモックオブジェクトは、期待される結果でプログラムされていることに注意してください。

27
Robert Harvey

モックオブジェクトは、実際のオブジェクトの動作を模倣するシミュレートされたオブジェクトです。通常、次の場合にモックオブジェクトを作成します。

  • 実際のオブジェクトは、ユニットテストに組み込むには複雑すぎます(たとえば、ネットワーク通信では、他のピアであるとシミュレートするモックオブジェクトを使用できます)
  • オブジェクトの結果は非決定的です
  • 実際のオブジェクトはまだ利用できません
15
Dani Cricco

Mockオブジェクトは Test Double の一種です。モックオブジェクトを使用して、テスト中のクラスと他のクラスのプロトコル/相互作用をテストおよび検証します。

通常、「プログラム」または「記録」のような期待があります。クラスが基礎となるオブジェクトに対して行うことを期待するメソッド呼び出しです。

たとえば、ウィジェット内のフィールドを更新するサービスメソッドをテストしているとします。そして、あなたのアーキテクチャには、データベースを扱うWidgetDAOがあります。データベースとの対話は遅く、セットアップとその後のクリーニングは複雑なので、WidgetDaoのモックを作成します。

サービスが何をしなければならないか考えてみましょう。データベースからウィジェットを取得し、それを使って再度保存します。

したがって、擬似モックライブラリを使用した擬似言語では、次のようになります。

Widget sampleWidget = new Widget();
WidgetDao mock = createMock(WidgetDao.class);
WidgetService svc = new WidgetService(mock);

// record expected calls on the dao
expect(mock.getById(id)).andReturn(sampleWidget);   
expect(mock.save(sampleWidget);

// turn the dao in replay mode
replay(mock);

svc.updateWidgetPrice(id,newPrice);

verify(mock);    // verify the expected calls were made
assertEquals(newPrice,sampleWidget.getPrice());

このようにして、他のクラスに依存するクラスの開発を簡単にテストできます。

12
Peter Tillemans

Martin Fowlerによるすばらしい記事 をお勧めします。モックとは何か、スタブとどのように違うのかを説明します。

11
Adam Byrtek

コンピュータープログラムの一部を単体テストするときは、その特定の部分の動作のみをテストすることが理想的です。

たとえば、印刷を呼び出すために別のプログラムを使用するプログラムの架空の部分から、以下の擬似コードを見てください。

If theUserIsFred then
    Call Printer(HelloFred)
Else
   Call Printer(YouAreNotFred)
End

これをテストする場合、主にユーザーがフレッドかどうかを調べる部分をテストする必要があります。本当にPrinter部分をテストする必要はありません。それは別のテストになります。

Thisは、Mockオブジェクトの出番です。他のタイプのふりをします。この場合、Mock Printerを使用して、実際のプリンターのように機能するようにしますが、印刷などの不便なことはしません。


モックではない、使用できるふりをするオブジェクトには他にもいくつかのタイプがあります。 Mocks Mocksを作成する主なことは、ビヘイビアーと期待に基づいて構成できることです。

期待値を使用すると、モックが誤って使用されたときにエラーが発生します。したがって、上記の例では、「user is Fred」テストケースでHelloFredを使用してPrinterが確実に呼び出されるようにすることができます。そうでない場合、モックは警告を発します。

Mocksの動作は、たとえば、コードで次のようなことをしたことを意味します。

If Call Printer(HelloFred) Returned SaidHello Then
    Do Something
End

ここで、プリンターが呼び出されてSaidHelloが返されたときにコードが何をするかをテストしたいので、HelloFredで呼び出されたときにSaidHelloを返すようにモックをセットアップできます。

これに関する優れたリソースの1つは、Martin Fowlersの投稿です Mocks Are n't Stubs

9
David Hall

モックオブジェクトとスタブオブジェクトは、単体テストの重要な部分です。実際、これらは、ユニットのgroupsではなくnitsをテストしていることを確認するのに大いに役立ちます。

一言で言えば、スタブを使用して、SUT(テスト対象システム)の他のオブジェクトへの依存関係を解除し、モックを行いますおよび SUTが依存関係の特定のメソッド/プロパティを呼び出したことを確認します。これは、単体テストの基本原則に戻ります。テストは、読みやすく、高速で、構成を必要とせず、すべての実際のクラスを使用することで暗示されるはずです。

一般に、テストには複数のスタブを含めることができますが、モックは1つだけにする必要があります。これは、モックの目的は動作を検証することであり、テストでは1つのことのみをテストする必要があるためです。

C#とMoqを使用した簡単なシナリオ:

_public interface IInput {
  object Read();
}
public interface IOutput {
  void Write(object data);
}

class SUT {
  IInput input;
  IOutput output;

  public SUT (IInput input, IOutput output) {
    this.input = input;
    this.output = output;
  }

  void ReadAndWrite() { 
    var data = input.Read();
    output.Write(data);
  }
}

[TestMethod]
public void ReadAndWriteShouldWriteSameObjectAsRead() {
  //we want to verify that SUT writes to the output interface
  //input is a stub, since we don't record any expectations
  Mock<IInput> input = new Mock<IInput>();
  //output is a mock, because we want to verify some behavior on it.
  Mock<IOutput> output = new Mock<IOutput>();

  var data = new object();
  input.Setup(i=>i.Read()).Returns(data);

  var sut = new SUT(input.Object, output.Object);
  //calling verify on a mock object makes the object a mock, with respect to method being verified.
  output.Verify(o=>o.Write(data));
}
_

上記の例では、Moqを使用してスタブとモックを示しました。 Moqは両方に同じクラスを使用します-_Mock<T>_少し混乱させます。とにかく、実行時に、データがparameterとして_output.Write_が呼び出されない場合、テストは失敗しますが、input.Read()の呼び出しが失敗しても失敗しません。

7
Igor Zevaka

Mocks Are n't Stubs 」へのリンクを介して示唆される別の答えとして、モックは実際のオブジェクトの代わりに使用する「テストダブル」の形式です。スタブオブジェクトなどの他の形式のテストダブルと異なる点は、他のテストダブルが状態検証(およびオプションでシミュレーション)を提供し、モックが動作検証(およびオプションでシミュレーション)を提供することです。

スタブを使用すると、スタブで複数のメソッドを任意の順序で(または繰り返し)呼び出して、スタブが意図した値または状態をキャプチャした場合に成功を判断できます。対照的に、モックオブジェクトは、特定の順序で、特定の回数でさえ、非常に特定の関数が呼び出されることを期待しています。メソッドが異なるシーケンスまたはカウントで呼び出されたため、モックオブジェクトを使用したテストは「失敗」と見なされます。テストが終了したときにモックオブジェクトが正しい状態にあったとしてもです。

このように、モックオブジェクトは、スタブオブジェクトよりもSUTコードに密接に結合していると見なされることがよくあります。検証しようとしているものに応じて、それは良いことも悪いこともあります。

4
Brent Arias

モックオブジェクトを使用するポイントの一部は、仕様に従って実際に実装する必要がないことです。彼らは単にダミーの応答を与えることができます。例えば。コンポーネントAとBを実装する必要があり、両方が互いに「呼び出し」(相互作用)する場合、Bが実装されるまでAをテストすることはできません。逆も同様です。テスト駆動開発では、これは問題です。したがって、AとBのモック(「ダミー」)オブジェクトを作成します。これらは非常に単純ですが、やり取りされるとsome一種の応答を与えます。そうすれば、Bのモックオブジェクトを使用してAを実装およびテストできます。

3
LarsH

Phpとphpunitについては、phpunitのドキュメントで詳しく説明されています。こちらをご覧ください phpunit documentation

単純なWordでは、モックオブジェクトは元のオブジェクトの単なるダミーオブジェクトであり、その戻り値を返します。この戻り値はテストクラスで使用できます。

1
Gautam Rai