Moq Verify without It - what kind of compare?
Asked Answered
A

1

9

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?

Ataghan answered 25/6, 2014 at 9:5 Comment(0)
B
7

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>());
    }
}
Brendon answered 25/6, 2014 at 10:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.