Rhino Mocks の新しい AAA 構文については多くの混乱があったことは承知していますが、正直に言うと、これまで見た限りでは気に入っています。読みやすくなり、キーストロークも節約できます。
ListController
基本的に、私はいくつかのリストを担当する をテストしています:) 最終的に DAL になるインターフェースを作成しましたが、これはもちろん今のところスタブ化されています。
次のようなコードがありました:
(manager
はテスト対象のシステム、data
はスタブ化されたデータ インターフェイスです)
[Fact]
public void list_count_queries_data()
{
data.Expect(x => x.ListCount(1));
manager.ListCount();
data.VerifyAllExpectations();
}
このテストの主な目的は、マネージャーが実際に DAL をクエリしていることを確認することです。DAL は実際には存在しないため、「実際の」値は返されないことに注意してください。
ただし、次のように戻り値を持つように期待値を変更する必要があるため、これは失敗します。
data.Expect(x => x.ListCount(1)).Return(1);
これで問題なく実行され、テストは合格します。しかし- 現時点で私が混乱しているのは、戻り値が意味するのは何もない100、50、42 などに変更しても、テストは常に合格しますか?
これは私を不安にさせます。なぜなら、テストは明示的である必要があり、予想される条件が満たされない場合は完全に失敗するはずだからです。
テストを次のように変更します (「1」はカウントがリンクされる予想される ID です):
[Fact]
public void list_count_queries_data()
{
manager.ListCount();
data.AssertWasCalled(x => x.ListCount(1));
}
すべて正常に合格し、テストを に切り替えるとAssertWasNotCalled
、予想どおりに失敗します。また、読みやすさが大幅に向上し、テスト対象が明確になり、最も重要なのは、予想どおりに合格および不合格になると思います。
それで、最初のコード例で何かが抜けているのでしょうか?スタブにアサーションを行うことについてどう思いますか?(興味深い議論がありましたここ、個人的には好きでしたこの応答。
ベストアンサー1
あなたのテストは何を達成しようとしているのですか?
どのような動作または状態を検証していますか? 具体的には、コラボレーター (データ) のメソッドがListCount
呼び出されていることを検証していますか (インタラクション ベースのテスト)、それとも、結果を他の場所で検証しながら、テスト対象のクラスを駆動するための定型値を返したいだけですかListCount
(従来の状態ベースのテスト)?
期待値を設定する場合は、モックと期待値を使用しますMockRepository.CreateMock<IMyInterface>()
。myMock.Expect(x => x.ListCount())
メソッドをスタブ化する場合は、MockRepository.CreateStub<IMyInterface>()
と を使用しますmyStub.Stub(x => x.ListCount())
。
(余談ですが、stub.AssertWasCalled() を使用すると、mock.Expect とほぼ同じことを、おそらくより読みやすい構文で実現できることは知っていますが、ここでは単にモックとスタブの違いについて詳しく説明します)。
Roy Osherove は、モックとスタブについて非常にわかりやすい説明をしています。
もっとコードを投稿してください!
スタブ(またはモック)の作成方法と、その結果がテスト対象のクラスでどのように使用されるかの完全な図が必要です。ListCount
入力パラメータはありますか?ある場合、それは何を表していますか?呼ばれる特定の価値?気にしますか?ListCount
戻り値特定の値ですか?
Simon Laroche が指摘したように、Manager が ListCount のモック/スタブされた戻り値を使用して実際に何も行わない場合、テストは成功も失敗もしません。テストで期待されるのは、モック/スタブされたメソッドが呼び出されることだけです。それ以上のことはありません。
問題をよりよく理解するために、次の 3 つの情報を考慮すると、すぐに理解できるようになります。
- 何がテストされているか
- どのような状況ですか?
- 期待される結果は何ですか?
比較する:モックを使用したインタラクションベースのテスト模擬試験の呼び出しはテスト。
[Test]
public void calling_ListCount_calls_ListCount_on_DAL()
{
// Arrange
var dalMock = MockRepository.Mock<IDAL>();
var dalMock.Expect(x => x.ListCount()).Returns(1);
var manager = new Manager(dalMock);
// Act
manager.ListCount();
// Assert -- Test is 100% interaction based
dalMock.VerifyAllExpectations();
}
スタブを使用した状態ベースのテストスタブはテストを実行しますが、期待値の一部ではありません。
[Test]
public void calling_ListCount_returns_same_count_as_DAL()
{
// Arrange
var dalStub = MockRepository.Stub<IDAL>();
var dalStub.Stub(x => x.ListCount()).Returns(1);
var manager = new Manager(dalMock);
// Act
int listCount = manager.ListCount();
// Assert -- Test is 100% state based
Assert.That(listCount, Is.EqualTo(1),
"count should've been identical to the one returned by the dal!");
}
私は可能な限り状態ベースのテストを好みますが、インタラクションベースのテストは、次のようなAPIで設計されている場合によく必要になります。聞くのではなく伝える検証するための公開された状態がなくなるため、注意してください。
API の混乱。モックはスタブではありません。それともそうでしょうか?
Rhino モックにおけるモックとスタブの区別は曖昧です。伝統的に、スタブは期待値を持つことを意図していません。そのため、テスト ダブルでメソッドが呼び出されなかったとしても、テストが直接失敗することはありません。
... ただし、Rhino Mocks API は強力ですが、スタブに期待値を設定することを可能にするため、混乱を招きます。私にとって、これは一般的な用語に反しています。私も、この用語はあまり重要視していません。私の意見では、この区別をなくし、テスト ダブルで呼び出されるメソッドが役割を設定する方がよいでしょう。