5

我有我认为应该是一个非常简单的测试用例,但是每次我运行它时 QTAgent32 都会死掉。在调试模式下运行测试用例显示System.StackOverflowException在“未知模块”中被抛出。我已将其缩小到表现出这种行为的最基本的实现(.NET 4 和 VS 2010 Ultimate):

[TestClass]
public class StackOverflow
{
    [TestMethod]
    public void CreateStackOverflow()
    {
        var mockMyType1 = new Mock<MyType>();
        mockMyType1.Setup(m => m.Equals(mockMyType1.Object)).Returns(true);

        var mockMyType2 = new Mock<MyType>();

        // Real test is for a filtering routine and the Assert is using
        // Contains(), but it uses Equals() internally so it has the same problem
        Assert.IsTrue(mockMyType1.Object.Equals(mockMyType1.Object)); // returns true
        Assert.IsFalse(mockMyType1.Object.Equals(mockMyType2.Object)); // explodes
    }
}

public class MyType
{
    public virtual bool IsActive { get; set; }
    public override bool Equals(object obj)
    {
        return false;  // Not the real implementation but irrelevant to this issue
    }
}

我觉得我错过了一些关于关闭或起订量的重要信息,但这似乎应该可行。我尝试过的事情,试图理解这个问题,但只会让我更加困惑:

  • 我尝试将 Equals() 设置替换为,mockMyType.Setup(m => m.Equals(m)).Returns(true);但这会导致 Moq 抛出 NotSupportedException
  • 如果我让 CallBase 为真而不是设置 Equals(),一切正常
  • 最后,如果 MyType 类没有覆盖 Equals(),那么一切正常。

谁能指出我可能发生的事情的方向?我完全不知所措。

编辑:我相信我有几个选择来完成这项工作(包括下面 Lanorkin 的回复),但我真的很想知道为什么会这样。我做错了什么,或者我应该提交 Moq 或 Visual Studio 中的错误吗?

更新:我最终选择了下面的 Denys 解决方案版本并向 Moq 提交错误报告。我的设置现在看起来像:

mockMyType1.Setup(m => m.Equals(It.Is<MyType>(x => ReferenceEquals(x, mockMyType1.Object)))).Returns(true);
4

3 回答 3

3

是的,模拟 Equals(object) 使其失败(使用 Reflector/dotPeek 查看更多):

最小起订量堆栈溢出

好消息 - 很容易解决。只需将 Equals 重载添加到MyType类,从而模拟Equals(MyType)而不是Equals(object)

    public virtual bool Equals(MyType obj)
    {
        return Equals((object)obj);
    }
于 2013-04-01T19:13:29.123 回答
1

我认为问题在于你这样做:

mockMyType.Setup(m => m.Equals(mockMyType.Object)).Returns(true);

仅针对同一对象的参数进行模拟。如果您使用任何其他参数,它将无法匹配。

请尝试这样做:

mockMyType.Setup(m => m.Equals(It.IsAny<MyType>())).Returns(true);

但是,您应该得到一个关于意外方法调用的不同异常。

于 2013-04-01T16:15:17.407 回答
1

这个问题从昨天开始就一直困扰着我,终于找到了答案。您必须在 setup 方法中使用一个函数,并且它应该断言模拟对象的真正相等性。我的意思是ReferenceEquals。所以我修改了你的GetMockMyTypes代码。当然它不能用作参考,但到目前为止意图很明确:

public static class MyTypeHelper
{
   public static IList<MyType> GetMockMyTypes()
   {
      var myTypes = new List<MyType>();

      var myMock1 = new Mock<MyType>().Object;
      Mock.Get(myMock1)
          .Setup(m => m.Equals(It.Is<MyType>(x => ReferenceEquals(x, myMock1))))
          .Returns(true);
      Mock.Get(myMock1).Setup(m => m.IsActive).Returns(false);
      myTypes.Add(myMock1);

      var myMock2 = new Mock<MyType>().Object;
      Mock.Get(myMock2)
          .Setup(m => m.Equals(It.Is<MyType>(x => ReferenceEquals(x, myMock2))))
          .Returns(true);
      Mock.Get(myMock2).Setup(m => m.IsActive).Returns(true);
      myTypes.Add(myMock2);

      return myTypes;
   }
}
于 2013-04-02T06:38:48.273 回答