Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq Verify without It - what kind of compare?

Tags:

c#

moq

verify

When using Moq with Verify, to assert that a certain method has been called with specified parameters, different kind of syntax is possible; one is the "It" syntax, like this

mock.Verify(c => c.SomeMethod(It.Is<string>(s => s == ExpectedString)));

What happens here is that the parameter that SomeMethod is called with, is checked for equality with ExpectedString. Another possible syntax is without "It":

mock.Verify(c => c.SomeMethod(ExpectedString));

which should give the same result. From what I have been able to find on different forums, the difference is that the latter is an identify check (reference equals) (except for value types).

However, my question is when the parameter is of a type Collection type. In .NET, Equals on Collection<T> is just inherited from object, so the following verify:

mock.Verify(c => c.SomeMethod(new Collection<string> { ExpectedString }));

should not be possible to pass, given that the collection is instantiated in the verify, and thus cannot possibly be the same instance that is instantiated in the production code. Nevertheless, it works, which indicates that Moq does a CollectionAssert or something like that, contrary to what information I could find.

Here is a code example that illustrates the behaviour, the test passes, but I think it should fail if Moq had used reference equals comparison.

[TestMethod]
public void Test()
{
    var mock = new Mock<IPrint>();
    const int ExpectedParam = 1;
    var test = new TestPrinter { Printer = mock.Object, Expected = ExpectedParam };

    test.Do();

    mock.Verify(c => c.Print(new Collection<int> { ExpectedParam }));
}

public interface IPrint
{
    void Print(Collection<int> numbers);
}

public class TestPrinter
{
    public IPrint Printer { get; set; }

    public int Expected { get; set; }

    public void Do()
    {
        Printer.Print(new Collection<int> { Expected });
    }
}

Does anyone know if this is expected behaviour of Moq (version 4.1)? Was the behaviour changed at some version level?

like image 232
user3774315 Avatar asked Jun 25 '14 09:06

user3774315


1 Answers

This is desired behaviourand was added to moq in January 2009 (version 3.0.203.1).

If moq finds an IEnumerable, it uses SequenceEqual to compare the actual argument and the argument used in the setup, otherwise it just uses Equals.

Here's the relevant bit of code:

internal class ConstantMatcher : IMatcher
{
    ...

    public bool Matches(object value)
    {
        if (object.Equals(constantValue, value))
        {
            return true;
        }

        if (this.constantValue is IEnumerable && value is IEnumerable)
        {
            return this.MatchesEnumerable(value);
        }

        return false;
    }

    private bool MatchesEnumerable(object value)
    {
        var constValues = (IEnumerable)constantValue;
        var values = (IEnumerable)value;
        return constValues.Cast<object>().SequenceEqual(values.Cast<object>());
    }
}
like image 88
sloth Avatar answered Sep 23 '22 09:09

sloth