少し前に、 MartinFowlerによるMocksAre n't Stubsの記事を読みましたが、複雑さの追加に関して外部の依存関係が少し怖いことを認めなければならないので、質問したいと思います。
単体テストで使用するのに最適な方法は何ですか?
テスト対象のメソッドの依存関係を自動的にモックするために、常にモックフレームワークを使用する方がよいでしょうか、それとも、たとえばテストスタブなどのより単純なメカニズムを使用する方がよいでしょうか。
少し前に、 MartinFowlerによるMocksAre n't Stubsの記事を読みましたが、複雑さの追加に関して外部の依存関係が少し怖いことを認めなければならないので、質問したいと思います。
単体テストで使用するのに最適な方法は何ですか?
テスト対象のメソッドの依存関係を自動的にモックするために、常にモックフレームワークを使用する方がよいでしょうか、それとも、たとえばテストスタブなどのより単純なメカニズムを使用する方がよいでしょうか。
マントラが進むにつれ、「おそらく機能する可能性のある最も単純なものを使用してください」。
モックはテストをもろくするので、常にモックを使用することは避けてください。モックされたインターフェースまたは実装が変更された場合、テストは実装によって呼び出されるメソッドに関する複雑な知識を持っています...テストは失敗します。これは悪いことです。SUTを実行するだけでなく、テストを実行するために追加の時間を費やすことになります。テストは、実装と不適切に親密であってはなりません。
だからあなたの最善の判断を使ってください..それが私が書くのを節約するのを助けるとき、私はモックを好みます-n>>3メソッドで偽のクラスを更新します。
エピローグ/審議の更新
:(モキストテストの例としてToranBillupsに感謝します。以下を参照してください)
こんにちはダグ、まあ、私たちは別の聖なる戦争に超越したと思います-クラシックTDDers対モキストTDDers。私は前者に所属していると思います。
私は通常、期待のためにモックを使用することを好みます。値を返すスタブでメソッドを呼び出すと、通常は値が返されるだけです。しかし、モックでメソッドを呼び出すと、値が返されるだけでなく、メソッドが最初に呼び出されたという設定も強制されます。つまり、期待値を設定してからそのメソッドを呼び出さないと、例外がスローされます。期待値を設定するということは、本質的に「このメソッドが呼び出されない場合は、何か問題が発生した」ということです。逆に言えば、モックでメソッドを呼び出して期待値を設定しなかった場合、例外がスローされ、基本的に「期待していなかったときにこのメソッドを呼び出して何をしているのですか」というメッセージが表示されます。
呼び出しているすべてのメソッドに期待を持たせたくない場合があるため、一部のモッキング フレームワークでは、設定した期待のみが適用され、他のすべてのメソッド呼び出しが処理されるという点で、モック/スタブ ハイブリッドのような「部分的な」モックを許可します。値を返すだけという点でスタブに似ています。
ただし、スタブを使用する有効な場所の 1 つは、レガシ コードにテストを導入する場合です。場合によっては、すべてをリファクタリングしてモックを簡単に、または可能にするよりも、テストしているクラスをサブクラス化してスタブを作成する方が簡単な場合があります。
そしてこれに…
モックはテストを脆弱にするため、常に使用しないでください。モック化されたインターフェイスが変更された場合、テストは実装によって呼び出されるメソッドの複雑な知識を持つようになりました... テストが壊れます。だからあなたの最善の判断を下してください.. <
...インターフェイスが変更された場合、テストは中断したほうがよいと思います。単体テストの要点は、現在存在するコードを正確にテストすることだからです。
それは、実行しているテストの種類によって異なります。動作ベースのテストを行っている場合は、依存関係との相互作用が発生することを確認できるように、動的モックが必要になる場合があります。ただし、状態ベースのテストを行っている場合は、値などを確認するためにスタブが必要になる場合があります
たとえば、以下のテストでは、プロパティ値が設定されていることを確認できるようにビューをスタブ化しています (状態ベースのテスト)。次に、サービス クラスの動的モックを作成して、テスト (相互作用/動作ベースのテスト) 中に特定のメソッドが確実に呼び出されるようにします。
<TestMethod()> _
Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False()
mMockery = New MockRepository()
mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView)
mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService)
mPresenter = New ProductPresenter(mView, mProductService)
Dim ProductList As New List(Of Product)()
ProductList.Add(New Product())
Using mMockery.Record()
SetupResult.For(mView.PageIsPostBack).Return(False)
Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once()
End Using
Using mMockery.Playback()
mPresenter.OnViewLoad()
End Using
'Verify that we hit the service dependency during the method when postback is false
Assert.AreEqual(1, mView.Products.Count)
mMockery.VerifyAll()
End Sub
組み合わせて使用するのが最善であり、独自の判断を使用する必要があります。私が使用するガイドラインは次のとおりです。
2 番目のモックは、一種の必要悪です。ここで実際に起こっていることは、スタブを使用するかモックを使用するかに関係なく、場合によっては必要以上にコードに結合する必要があるということです。そのような場合は、スタブよりもモックを使用することをお勧めします。これは、その結合がいつ壊れて、コードがテストで想定したとおりに記述されなくなったかがわかるためです。これを行うときは、テストにコメントを残しておくのがおそらく最善です。
繰り返しますが、これはコードの匂いであり、最後の手段です。これを頻繁に行う必要がある場合は、テストの書き方を考え直してみてください。
Statist と Interaction は気にしないでください。役割と関係について考えてみましょう。オブジェクトが隣人と協力して仕事を完了する場合、その関係 (インターフェースで表現されている) は、モックを使用したテストの候補です。オブジェクトが少し動作する単純な値オブジェクトである場合は、直接テストしてください。モック (またはスタブ) を手で書く意味がわかりません。それが私たち全員がそれから始めてリファクタリングした方法です。
より長い議論については、http://www.mockobjects.com/bookをご覧ください。