4

次のテスト中のクラス(および関連するDTOクラスとインターフェイス)があるとします。

public class Foo
{
    private readonly IBar _bar;

    public Foo(IBar bar) { _bar = bar; }

    public void DoStuff()
    {
        var dto = new DTO();

        dto.Num = 1;
        _bar.Test(dto);

        dto.Num = 2;
        _bar.Test(dto);
    }
}

public class DTO { public int Num { get; set; } }

public interface IBar { void Test(DTO dto); }

そして、このテストメソッド(IBar.Test()が2回呼び出されることを確認しようとします:1回はNum = 1で、もう1回はNum = 2で):

public void TestMethod1()
{
    var bar = A.Fake<IBar>();
    var foo = new Foo(bar);
    foo.DoStuff();

    A.CallTo(() => bar.Test(A<DTO>.That.Matches(x => x.Num == 1))).MustHaveHappened();
    A.CallTo(() => bar.Test(A<DTO>.That.Matches(x => x.Num == 2))).MustHaveHappened();
}

最初の「MustHaveHappened」呼び出しは失敗します。IBar.Test()の両方の呼び出しで使用されるDTOが同じインスタンスであることが原因であることがわかりました。2つの異なるDTOを使用してIBar.Test()を呼び出すようにコードを変更すると、期待どおりに機能します。

私の質問は:これはFakeItEasyのバグですか、それとも私は何か間違ったことをしていますか?

4

1 に答える 1

7

これは正しい動作であり、バグではありません。FakeItEasyは引数付きの呼び出しを記録しますが、呼び出し中の引数の内部状態は保存しません。引数自体の参照/値を保存するだけです。最後に、検証フェーズ中、DTOオブジェクトの現在の状態はNum2に等しい状態になり、それに対してFakeItEasyが検証します。

このような場合にすぐにサポートできるかどうかはわかりませんが、(2番目のDTOオブジェクトを作成せずに)この回避策を簡単に実装できます。

var bar = A.Fake<IBar>();
var foo = new Foo(bar);
var expectedNumValues = new [] { 1, 2 };
var actualNumValues = new List<int>();
// Whenever a call to IBar.Test is made, store DTO.Num in list
A.CallTo(() => bar.Test(A<DTO>.Ignored)).Invokes(
    fakeCall =>
    {
        var dto = (DTO) fakeCall.Arguments[0];
        actualNumValues.Add(dto.Num);
    }
);

foo.DoStuff();

// this verifies that both collections contain same elements at same positions
CollectionAssert.AreEqual(expectedNumValues, actualNumValues);
于 2012-10-12T10:20:39.107 に答える