Verifying calls with ref parameters using Moq
Asked Answered
L

2

7

How can I verify that the "CallWithRef" method was called using Moq?

public interface ITest
{
    void CallWithoutRef(string value, List<string> errors);
    void CallWithRef(string value, ref List<string> errors);
}

public class Foo
{
    private ITest testInterface;

    public Foo(ITest testInterface)
    {
        this.testInterface = testInterface;
    }

    public void DoStuff(string value)
    {
        var errorList = new List<string>();
        testInterface.CallWithoutRef(value, errorList);
        testInterface.CallWithRef(value, ref errorList);
    }
}

[TestMethod]
public void VerifyTestInterfaceCalls()
{
    var expectedValue = Path.GetRandomFileName();
    var mockTestInterface = new Mock<ITest>();
    var foo = new Foo(mockTestInterface.Object);

    foo.DoStuff(expectedValue);
    mockTestInterface.Verify(x => x.CallWithoutRef(expectedValue, It.IsAny<List<string>>()));

    // Test fails here:
    var errorList = It.IsAny<List<string>>();
    mockTestInterface.Verify(x => x.CallWithRef(expectedValue, ref errorList));
}
Lou answered 11/9, 2014 at 11:11 Comment(0)
S
8

This has changed for the better in Moq 4.8.0, see the other answer here for details on It.Ref!


Calls to Verify in Moq perform a strict equality check for ref arguments. When the argument is a reference type (like in your example), the argument matcher that Moq uses succeeds only if the actual and expected values are the same reference. This is because it uses object.ReferenceEquals(expected, actual) to verify equality.

This behavior is mentioned (although it could be a little more thorough) in the Moq Quickstart:

// ref arguments
var instance = new Bar();
// Only matches if the ref argument to the invocation is the same instance
mock.Setup(foo => foo.Submit(ref instance)).Returns(true);

In your example, It.IsAny<List<string>>() actually returns default(T) in the end, so you're comparing null to the new instance of a List<string> created in DoStuff, which according to the matcher's implementation will fail.

This is obviously a toy example so I can't suggest what you should do, but if you modify DoStuff to accept the list rather than create its own, you could test it like this:

var errorList = It.IsAny<List<string>>(); 
// var errorList = new List<string>();   // also works

foo.DoStuff(expectedValue, errorList);

mockTestInterface.Verify(x => x.CallWithoutRef(expectedValue, It.IsAny<List<string>>()));
mockTestInterface.Verify(x => x.CallWithRef(expectedValue, ref errorList));
Sanguinolent answered 11/9, 2014 at 12:57 Comment(3)
if you are looking at this answer and you have v4.8.0+ don't be like me and not notice the answer below https://mcmap.net/q/1426851/-verifying-calls-with-ref-parameters-using-moq by @Spencer-SuttonBrindabrindell
Thanks @ShereefMarzouk, I added some text to point to the other answer.Sanguinolent
Awesome, Thanks!Brindabrindell
F
7

As of version 4.8.0 this is now possible using Moq. Here's the link: https://github.com/moq/moq4/issues/479

You would pass errorList in like this

ref It.Ref<List<string>>.IsAny
Foothill answered 28/3, 2018 at 14:21 Comment(1)
P.S. If you are using the mockedBla.Protected() and using moq.Protected; you should use ItExpr instead of ItBrindabrindell

© 2022 - 2024 — McMap. All rights reserved.