3

I'm using the Moq framework for unit tests, and I came across this interesting problem.

public interface Bar : IEquatable<Bar>
{
}

[TestClass]
public class TestClass
{
    Mock<Bar> a;
    Mock<Bar> b;

    public TestClass()
    {
        a = new Mock<Bar>();
        b = new Mock<Bar>();

        a.Setup(bar => bar.Equals(b.Object)).Returns(true);
    }

    [TestMethod]
    public void AssertEqualsTest()
    {
        Assert.AreEqual(a.Object, b.Object); //fails
    }

    [TestMethod]
    public void AssertIsTrueTest()
    {
         Assert.IsTrue(a.Object.Equals(b.Object)); //passes
    }
}

First Issue

So Assert.AreEqual just fails. I don't want to have to use the line from the second test every time I need to check equality even though most (if not all) of my classes inherit from IEquatable.

You might think it fails because Setup is only setting the IEquality.Equals() function (which Assert.AreEqual probably doesn't check), but if you add the line

a.Setup(x => x.Equals((object)b.Object)).Returns(true);

to the constructor, it still fails.

Second Issue

If you comment out the : IEquatable<Bar> from the interface declaration (so that a.Setup overwrites object.Equals), both tests fail.

My desired outcome is to be able to setup equals on a Mock object and call Assert.AreEqual.

hehewaffles
  • 582
  • 5
  • 17
  • 4
    Why do you want to compare the objects generated mock a `Mock`? – JaredPar May 31 '12 at 01:08
  • To ask @JaredPar's question in a different way, what is the object / unit under test? – manojlds May 31 '12 at 03:35
  • It's to test an equality operator on another class. The function kept failing, so I stepped into it and noticed that the two weren't equal like I thought they should have been. – hehewaffles May 31 '12 at 13:20

2 Answers2

4

First issue

Checked via dotPeek. Assert.AreEqual calls the static method object.Equals to compare the instances. object.Equals uses operator == first and since the mock instance does not implement that operator, this will default to comparing references. Clearly, a and b are different instances so the comparison returns false.

Second issue

I haven't looked at the internals of Moq but I assume this happens because the interface does not declare an Equals method. Confirmed with the following (which succeeds):

public interface IBar
{
}

public class Bar : IBar
{
    public override bool Equals(object obj)
    {
        return false;
    }
}

[TestClass]
public class Class1
{
    [TestMethod]
    public void TestMoq()
    {
        var a = new Mock<Bar>();
        var b = new Mock<Bar>();

        a.Setup(bar => bar.Equals(b.Object)).Returns(true);

        Assert.IsTrue(a.Object.Equals(b.Object));
    }
}

If I remove the Bar.Equals override, the test will also fail. Just a guess but since Moq uses Castle internally, this issue can be explained by this Q&A.

Anyway, I think what you're doing now with Assert.IsTrue(a.Object.Equals(b.Object)); and IEquatable is a sufficient workaround for this.

By the way, as JaredPar asked above, why are you comparing mocks?

Community
  • 1
  • 1
Ilian
  • 5,113
  • 1
  • 32
  • 41
2

I used..

mockLine2.CallBase = True

And Why?

We're testing an order service. As order has two lines, service removes any line matching some criteria. Lines are an IList, List (etc) uses .equals() to find the item you want, so to remove the line, you need an implementation of equals that passes:

Assert.IsTrue(mockLine2.Object.Equals(mockLine2.Object)

"Moq mock isn't detecting that a line equals itself, so the line collection on the order will fail to find the line.")

You could argue that as Order isn't the class under test we should mock the order and assert that order.Lines.Remove() is called with the right line, but we've found it on balance less painful to use the real domain objects (they're all fairly dumb) than mock them.

Adi Lester
  • 24,731
  • 12
  • 95
  • 110
Paul
  • 41
  • 1