Moq: How to get to a parameter passed to a method of a mocked service

C#Moq

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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)));

Solution 2 - C#

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()

Solution 3 - C#

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.

Solution 4 - C#

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; }
}

Solution 5 - C#

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.

Solution 6 - C#

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 )));

Solution 7 - C#

This also works:

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

Solution 8 - C#

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!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJanView Question on Stackoverflow
Solution 1 - C#GamlorView Answer on Stackoverflow
Solution 2 - C#JohnnyView Answer on Stackoverflow
Solution 3 - C#JavaJudtView Answer on Stackoverflow
Solution 4 - C#Andrew RadfordView Answer on Stackoverflow
Solution 5 - C#Pete MartinView Answer on Stackoverflow
Solution 6 - C#Ed YablonskyView Answer on Stackoverflow
Solution 7 - C#Jeff SmithView Answer on Stackoverflow
Solution 8 - C#Jacob McKayView Answer on Stackoverflow