このクラスを想像してください
_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
_に渡したかを確認できますか?
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)));
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());
検証は非常に強力であり、時間をかけて慣れる価値があります。
Gamlorの答えは私にとってはうまくいきましたが、複数のパラメーターを含むソリューションを探していたので、John Carpenterのコメントを拡張すると思いました。このページを偶然見つけた他の人たちも同じような状況にあると思いました。 Moq documentation でこの情報を見つけました。
Gamlorの例を使用しますが、AsyncHandleメソッドがstring
とSomeResponse
オブジェクトの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
メソッドに別のタイプを追加し、必要に応じてラムダ式を変更するだけです。
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; }
}
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 )));
別の方法は、moq
のCapture.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()
これも機能します:
Mock<InterfaceThing> mockedObject = new Mock<InterfaceThing>();
var objectParameter = mockedObject.Invocations[1].Arguments[0] as ObjectParameter;
ここにはたくさんの良い答えがあります!依存関係に渡されるいくつかのクラスパラメーターについてアサーションを作成する必要があるまで、すぐに使える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が同じことを達成する機能を提供し、宣言的であり、これが行う障害分離を提供する場合、私はうれしく思います。成功を祈っている!