Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verifying calls with ref parameters using Moq

Tags:

ref

moq

verify

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));
}
like image 655
ilitirit Avatar asked Sep 11 '14 11:09

ilitirit


1 Answers

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));
like image 197
Patrick Quirk Avatar answered Oct 13 '22 10:10

Patrick Quirk