web-dev-qa-db-ja.com

Moq:模擬サービスのメソッドに渡されたパラメーターを取得する方法

このクラスを想像してください

_public class Foo {

    private Handler _h;

    public Foo(Handler h)
    {
        _h = h;
    }

    public void Bar(int i)
    {
        _h.AsyncHandle(CalcOn(i));
    }

    private SomeResponse CalcOn(int i)
    {
        ...;
    }
}
_

FooのテストでMo(q)ckingハンドラー、どのようにしてBar()が__h.AsyncHandle_に渡したかを確認できますか?

142
Jan

Mock.Callback-methodを使用できます:

var mock = new Mock<Handler>();
SomeResponse result = null;
mock.Setup(h => h.AnsyncHandle(It.IsAny<SomeResponse>()))
    .Callback<SomeResponse>(r => result = r);

// do your test
new Foo(mock.Object).Bar(22);
Assert.NotNull(result);

渡された引数で簡単なものだけをチェックしたい場合は、直接行うこともできます:

mock.Setup(h => h.AnsyncHandle(It.Is<SomeResponse>(response => response != null)));
241
Gamlor

Gamlorの答えは動作しますが、それを行う別の方法(およびテストでより表現力があると考える方法)は...

var mock = new Mock<Handler>();
var desiredParam = 47; // this is what you want to be passed to AsyncHandle
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(desiredParam), Times.Once());

検証は非常に強力であり、時間をかけて慣れる価値があります。

19
Pete Martin

Gamlorの答えは私にとってはうまくいきましたが、複数のパラメーターを含むソリューションを探していたので、John Carpenterのコメントを拡張すると思いました。このページを偶然見つけた他の人たちも同じような状況にあると思いました。 Moq documentation でこの情報を見つけました。

Gamlorの例を使用しますが、AsyncHandleメソッドがstringSomeResponseオブジェクトの2つの引数を取るふりをしましょう。

_var mock = new Mock<Handler>();
string stringResult = string.Empty;
SomeResponse someResponse = null;
mock.Setup(h => h.AsyncHandle(It.IsAny<string>(), It.IsAny<SomeResponse>()))
    .Callback<string, SomeResponse>((s, r) => 
    {
        stringResult = s;
        someResponse = r;
    });

// do your test
new Foo(mock.Object).Bar(22);
Assert.AreEqual("expected string", stringResult);
Assert.IsNotNull(someResponse);
_

基本的には、適切なタイプの別のIt.IsAny<>()を追加し、Callbackメソッドに別のタイプを追加し、必要に応じてラムダ式を変更するだけです。

17
JavaJudt

Callbackメソッドは確かに機能しますが、多くのパラメーターを持つメソッドでこれを実行している場合、少し冗長になる可能性があります。これは、定型文の一部を削除するために使用したものです。

var mock = new Mock<Handler>();

// do your test   
new Foo(mock.Object).Bar(22);

var arg = new ArgumentCaptor<SomeResponse>();
mock.Verify(h => h.AsyncHandle(arg.Capture()));
Assert.NotNull(arg.Value);

ArgumentCaptorのソースは次のとおりです。

public class ArgumentCaptor<T>
{
    public T Capture()
    {
        return It.Is<T>(t => SaveValue(t));
    }

    private bool SaveValue(T t)
    {
        Value = t;
        return true;
    }

    public T Value { get; private set; }
}
14
Andrew Radford

It.Is<TValue>() matcherを使用できます。

var mock = new Mock<Handler>();
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(It.Is<SomeResponse>(r => r != null )));
4

別の方法は、moqCapture.In機能を使用することです。コレクションで引数のキャプチャを有効にするOOTB moq機能です。

//Arrange
var args = new List<SomeResponse>();
mock.Setup(h => h.AnsyncHandle(Capture.In(args)));

//Act
new Foo(mock.Object).Bar(22);

//Assert
//... assert args.Single() or args.First()
2
Johnny

これも機能します:

Mock<InterfaceThing> mockedObject = new Mock<InterfaceThing>();
var objectParameter = mockedObject.Invocations[1].Arguments[0] as ObjectParameter;
1
Jeff Smith

ここにはたくさんの良い答えがあります!依存関係に渡されるいくつかのクラスパラメーターについてアサーションを作成する必要があるまで、すぐに使えるMoq機能セットを使用します。ただし、そのような状況に陥った場合、It.Isマッチャーを使用したMoq Verify機能はテストの失敗を分離するのに適した機能を発揮せず、引数をキャプチャするReturn/Callbackの方法はテストに不要なコード行を追加します(そして長いテストは私にとっては行きません)。

ここに要点があります: https://Gist.github.com/Jacob-McKay/8b8d41ebb9565f5fca23654fd944ac6b Moq(4.12)拡張を使用して、アサーションを作成するためのより宣言的な方法を提供します前述の欠点なしで、モックに渡される引数について。確認セクションは次のようになります。

        mockDependency
            .CheckMethodWasCalledOnce(nameof(IExampleDependency.PersistThings))
            .WithArg<InThing2>(inThing2 =>
            {
                Assert.Equal("Input Data with Important additional data", inThing2.Prop1);
                Assert.Equal("I need a trim", inThing2.Prop2);
            })
            .AndArg<InThing3>(inThing3 =>
            {
                Assert.Equal("Important Default Value", inThing3.Prop1);
                Assert.Equal("I NEED TO BE UPPER CASED", inThing3.Prop2);
            });

Moqが同じことを達成する機能を提供し、宣言的であり、これが行う障害分離を提供する場合、私はうれしく思います。成功を祈っている!

0
Jacob McKay