web-dev-qa-db-ja.com

Rhino Mocks-スタブ.Expectと.AssertWasCalled

OK、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に切り替えると、予想どおり失敗します。また、テストの内容が明確になり、何がテストされているかがより明確になり、最も重要なのはPASSESとFAILSです。予想通り!

それで、最初のコード例で何か不足していますか?スタブでアサーションを作成することについてどう思いますか? (いくつかの興味深い議論がありました ここ 、私は個人的に気に入りました この応答

34
Rob Cooper

テストで達成しようとしていることは何ですか?

どのような動作または状態を確認していますか?具体的には、コラボレーター(データ)が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には入力パラメーターがありますか?もしそうなら、それは何を表していますか? と呼ばれる特定の値であるかどうかを気にしますか? ListCountreturnsが特定の値であるかどうかを気にしますか?

Simon Larocheが指摘したように、ManagerがListCountのモック/スタブされた戻り値で実際に何も実行していない場合、テストが成功したり失敗したりすることはありません。テストが期待するのは、モック/スタブされたメソッドが呼び出されることだけです。

問題をよりよく理解するために、3つの情報を検討してください。すぐにわかります。

  1. テストされているもの
  2. どのような状況で?
  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!");
}

私は個人的には可能な限り状態ベースのテストを優先しますが、公開された状態がないため、 Tell、Do n't Ask を考慮して設計されたAPIにはインタラクションベースのテストがしばしば必要です。検証する!

APIの混乱。モックはスタブではありません。それともそうですか?

サイモックのモックとスタブの違いは混乱しています。伝統的に、スタブは期待することを意図したものではありません。したがって、テストdoubleがそのメソッドを呼び出さなかった場合、これはテストを直接失敗させることはありません。

...ただし、Rhino Mocks APIは強力ですが、受け入れられている用語に反するスタブに期待を設定できるため、混乱を招きます。専門用語もあまり考えていません。私の意見では、区別が排除され、テストで呼び出されたメソッドが役割を二重に設定した方がよいでしょう。

53
Mark Simpson

私はそれがあなたのmanager.ListCount()が戻り値で何をしているのかと関係があると思います。

それを使用していない場合、DALは重要ではないものを返すことができます。

public class Manager
{
    public Manager(DAL data)
    { 
        this.data = data
    }
    public void ListCount()
    {
        data.ListCount(1); //Not doing anything with return value
        DoingSomeOtherStuff();
    }    
}

リストのカウントがその値で何かをしている場合は、それが何をしているのかをアサートする必要があります。例えば

Assert.IsTrue(manager.SomeState == "someValue");
1
Simon Laroche

使ってみましたか

data.AssertWasCalled(x => x.ListCount(1) = Arg.Is(EXPECTED_VALUE));
0
murki