Reputation: 6858
Imagine this class
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)
{
...;
}
}
Mo(q)cking Handler in a test of Foo, how would I be able to check what Bar()
has passed to _h.AsyncHandle
?
Upvotes: 255
Views: 170001
Reputation: 9529
An alternative is to use Capture.In
, which is an out-of-the-box feature in Moq that lets you capture arguments into a collection:
//Arrange
var args = new List<SomeResponse>();
mock.Setup(h => h.AsyncHandle(Capture.In(args)));
//Act
new Foo(mock.Object).Bar(22);
//Assert
//... assert args.Single() or args.First()
Upvotes: 96
Reputation: 13258
You can use the Mock.Callback-method:
var mock = new Mock<Handler>();
SomeResponse result = null;
mock.Setup(h => h.AsyncHandle(It.IsAny<SomeResponse>()))
.Callback<SomeResponse>(r => result = r);
// do your test
new Foo(mock.Object).Bar(22);
Assert.NotNull(result);
If you only want to check something simple on the passed in argument, you also can do it directly:
mock.Setup(h => h.AsyncHandle(It.Is<SomeResponse>(response => response != null)));
Upvotes: 370
Reputation: 2826
Lot's of good answers here! Go with the out of the box Moq feature-set until you need to make assertions about several class parameters passed to your dependencies. If you end up in that situation though, the Moq Verify feature with It.Is matchers doesn't do a good job of isolating the test failure, and the Returns/Callback way of capturing arguments adds unnecessary lines of code to your test (and long tests are a no-go for me).
Here is a gist: https://gist.github.com/Jacob-McKay/8b8d41ebb9565f5fca23654fd944ac6b with a Moq (4.12) extension I wrote that gives a more declarative way to make assertions about arguments passed to mocks, without the drawbacks aforementioned. Here is what the Verify section looks like now:
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);
});
I would be stoked if Moq provided a feature that accomplished the same thing while being as declarative and providing the failure isolation this does. Fingers crossed!
Upvotes: 3
Reputation: 119
You could use 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 )));
Upvotes: 11
Reputation: 119
This also works:
Mock<InterfaceThing> mockedObject = new Mock<InterfaceThing>();
var objectParameter = mockedObject.Invocations[1].Arguments[0] as ObjectParameter;
Upvotes: 10
Reputation: 3357
The Callback method will certainly work, but if you are doing this on a method with a lot of parameters it can be a bit verbose. Here is something that I have used to remove some of the boilerplate.
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);
Here is the source for 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; }
}
Upvotes: 35
Reputation: 859
Gamlor's answer worked for me, but I thought I would expand on John Carpenter's comment because I was looking for a solution involving more than one parameter. I figured other folks who stumble onto this page may be in a similar situation. I found this info in the Moq documentation.
I'll use Gamlor's example, but let's pretend the AsyncHandle method takes two arguments: a string
and a SomeResponse
object.
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);
Basically you just need to add another It.IsAny<>()
with the appropriate type, add another type to the Callback
method, and change the lambda expression as appropriate.
Upvotes: 52
Reputation: 856
Gamlor's answer works, but another way of doing it (and one which I consider to be more expressive in the test) is...
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());
Verify is very powerful, and worth taking the time to get used to.
Upvotes: 22