3

我正在使用 Moq 框架进行单元测试,我遇到了这个有趣的问题。

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
    }
}

首要问题

所以Assert.AreEqual只是失败了。我不想每次需要检查相等性时都使用第二个测试中的行,即使我的大多数(如果不是全部)类都继承自 IEquatable。

您可能认为它失败了,因为安装程序仅设置 IEquality.Equals() 函数(Assert.AreEqual可能不会检查),但如果您添加该行

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

对于构造函数,它仍然失败。

第二期

如果您从接口声明中注释掉: IEquatable<Bar>(以便a.Setup覆盖object.Equals),则两个测试都会失败。

我想要的结果是能够在Mock对象上设置 equals 并调用Assert.AreEqual.

4

2 回答 2

4

首要问题

通过 dotPeek 检查。Assert.AreEqual调用静态方法object.Equals来比较实例。object.Equals首先使用operator ==并且由于模拟实例没有实现该运算符,这将默认为比较引用。显然,a 和 b 是不同的实例,因此比较返回 false。

第二期

我没有看过 Moq 的内部结构,但我认为这是因为接口没有声明 Equals 方法。确认以下(成功):

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));
    }
}

如果我删除Bar.Equals覆盖,测试也将失败。只是一个猜测,但由于 Moq 在内部使用 Castle,这个问题可以通过这个Q&A来解释。

无论如何,我认为你现在正在做什么,Assert.IsTrue(a.Object.Equals(b.Object));并且IEquatable是一个足够的解决方法。

顺便说一句,正如 JaredPar 上面问的那样,你为什么要比较模拟?

于 2012-05-31T03:14:23.977 回答
2

我用了..

mockLine2.CallBase = True

为什么?

我们正在测试订单服务。由于订单有两行,服务删除任何符合某些条件的行。行是一个 IList,列表(等)使用 .equals() 来查找您想要的项目,因此要删除该行,您需要一个通过的 equals 实现:

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

“Moq mock 没有检测到一行等于自身,因此订单上的行集合将无法找到该行。”)

您可能会争辩说,由于 Order 不是被测试的类,我们应该模拟订单并断言order.Lines.Remove()用正确的行调用,但我们发现使用真实的域对象总体上不那么痛苦(它们都是公平的愚蠢)而不是嘲笑他们。

于 2012-09-19T10:23:07.800 回答