web-dev-qa-db-ja.com

Moq、厳密な使用と緩い使用

以前は、Rhino Mocksのみを使用し、典型的な厳密なモックを使用していました。私は現在、プロジェクトでMoqを使用しており、適切な使用法について疑問に思っています。

オブジェクトBuzzでBizzメソッドを呼び出すメソッドBarを持つオブジェクトFooがあるとします。

私のテストでは、Bizzが呼び出されていることを確認したいので、次の2つのオプションが考えられます。

厳格なモックで

var mockBuzz= new Mock<IBuzz>(MockBehavior.Strict);
mockBuzz.Setup(x => x.Bizz()); //test will fail if Bizz method not called
foo.Buzz = mockBuzz
foo.Bar();
mockBuzz.VerifyAll();

ゆるいモックで

var mockBuzz= new Mock<IBuzz>();    
foo.Buzz = mockBuzz
foo.Bar();
mockBuzz.Verify(x => x.Bizz()) //test will fail if Bizz method not called

これを行う標準または通常の方法はありますか?

51
Bryan Rowe

単体テストでモックを初めて使用するときは、厳格なモックを使用していました。これは長続きしませんでした。私がこれをやめた理由は本当に2つあります:

  1. テストはもろくなります-厳密なモックでは、セットアップメソッドが呼び出され、他のメソッドが呼び出されないことを2つ以上主張しています。コードをリファクタリングすると、テストしようとしていることがまだ真実であっても、テストが失敗することがよくあります。
  2. テストは読みにくい-テストしたいものに実際には関係がない場合でも、モックで呼び出されるすべてのメソッドの設定が必要です。誰かがこのテストを読んでも、テストにとって何が重要で、何が実装の副作用であるかを伝えるのは難しいです。

これらのため、ユニットテストではルーズモックを使用することを強くお勧めします。

77
Andy Lowry

私はC++ /。NET以外の開発のバックグラウンドがあり、最近.NETに詳しいので、初めてMoqを使用するときにある程度の期待がありました。私はテストでWTFが行われていること、およびテストしているコードが、コードが呼び出しを試みている関数をMockライブラリーに通知するのではなく、ランダムな例外をスローする理由を理解しようとしました。そのため、私はStrict動作をオンにする必要があることを発見しました。これは当惑するものでした-そして、私はまだ答えが確認されていないのを見たこの質問に遭遇しました。

Looseモード、およびそれがデフォルトであること非常識です。あなたが明示的にリストしていない完全に予測不可能なことを行うMockライブラリのポイントは一体何ですか?

ルーズモードをサポートするために、他の回答にリストされているポイントに完全に同意しません。それを使用する正当な理由はなく、私は今まで使いたくありません。単体テストを作成するとき、何が起こっているのかを確認したいと思います。関数がnullを返す必要があることがわかっている場合は、それを返すようにします。テストを(重要な意味で)脆弱にして修正し、テストコードスイートにセットアップ行を追加します。これは、ソフトウェアが何をするかを正確に説明する明示的な情報であるセットアップ行を追加します。

問題は、これを行うための標準的で通常の方法はありますか?

はい-一般的なプログラミングの観点から、つまり他の言語や.NETの世界以外では、常にStrictを使用する必要があります。 Goodnessは、それがMoqのデフォルトではない理由を知っています。

15
Benedict

私は簡単な慣習を持っています:

  1. テスト対象のシステム(SUT)が、自身に渡される引数にビジネスロジックを実際に変更したり適用したりせずに、基になるモック層に呼び出しを委任している場合は、厳密なモックを使用します。

  2. SUTがそれ自体に渡される引数にビジネスロジックを適用し、いくつかの派生/変更された値をモック層に渡す場合は、ルーズモックを使用します。

たとえば、次の2つのメソッドを持つデータベースプロバイダーStudentDALがあるとします。

データアクセスインターフェイスは次のようになります。

public Student GetStudentById(int id);
public IList<Student> GetStudents(int ageFilter, int classId);

このDALを使用する実装は次のようになります。

public Student FindStudent(int id)
{
   //StudentDAL dependency injected
   return StudentDAL.GetStudentById(id);
   //Use strict mock to test this
}
public IList<Student> GetStudentsForClass(StudentListRequest studentListRequest)
{
  //StudentDAL dependency injected
  //age filter is derived from the request and then passed on to the underlying layer
  int ageFilter = DateTime.Now.Year - studentListRequest.DateOfBirthFilter.Year;
  return StudentDAL.GetStudents(ageFilter , studentListRequest.ClassId)
  //Use loose mock and use verify api of MOQ to make sure that the age filter is correctly passed on.

}
9
Amol

私個人的には、モックを始めたばかりで、MoqはStrictモードから始めることで内部と何が起こっているかをよりよく理解できると感じています。 "Loose"は、詳細を隠し、moq初心者が見落とす可能性があるテストに合格することがあります。モックスキルを下げた後-おそらく、「セットアップ」で行を保存し、代わりに「検証」を使用するだけのように、緩い方がはるかに生産性が高くなります。

4
mithun_daa