Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock `object.Equals(object obj)` for an interface using Moq

Tags:

c#

.net

moq

I have an interesting problem to wrap your head around. Consider some interface like this one:

public interface IMyThing
{
    int Id { get; }
}

Now I want to test code that uses this interface. Maybe something with some LINQ magic. Something like this:

public class SomeClass
{
    private IMyThing _thing;

    ...

    public bool HasThing(IEnumerable<IMyThing> things)
    {
        return things.Contains(_thing);
    }
}

I'm mocking all objects implementing IMyThing using Moq:

public static IMyThing MockMyThing(int newId)
{
    var mock = new Mock<IMyThing>();
    mock.Setup(s => s.Id).Returns(newId);
    mock.Setup(s => s.Equals(It.IsAny<object>())).Returns<object>(obj =>
    {
        if (typeof(IMyThing).IsAssignableFrom(obj.GetType()))
        {
            return ((IMyThing)obj).Id == newId;
        }

        return false;
    });

    return mock.Object;
}

Here is the thing. The above code compiles without warnings but will never work. Moq creates an interceptor for the Equals() method but it is never reached. Instead, the equals method of the object proxy is called. I'm blaming the fact that I'm mocking an interface but not a concrete class.

UPDATE: Just realised Moq doesn't even create an interceptor.

Of course I could augment the IMyThing interface like this:

public interface IMyThing : IEquatable<IMyThing>
{
    int Id { get; }
}

The LINQ operator would recognize the IEquatable<T> interface and use it.

I don't want to do this because:

  • This is only usable with other IMyThing objects
  • IEquatable<T> was not meant for this purpose
  • I don't want to taint my Model only to make it mockable

How would you solve this?

like image 836
koloman Avatar asked Feb 15 '16 10:02

koloman


1 Answers

I ended up contributing code to the Moq project on Github (see issue #248). With this changes it is possible to mock object.Equals(object obj), object.GetHashCode() and object.ToString(), even for interface mocks.

Lets see if it gets accepted.

like image 129
koloman Avatar answered Oct 02 '22 15:10

koloman