CollectionAssert.AreEqual传递但CollectionAssert.AreEquivalent失败?

时间:2013-04-06 09:00:48

标签: vb.net unit-testing assert

我已经构建了一些复杂的对象,我试图通过进行一些单元测试来验证它是否正常工作。这涉及比较一些List(Of T),所以我尝试使用CollectionAssert。 现在我遇到了一些奇怪的东西。

首先,我使用CollectionAssert.AreEqual来查看第一个列表是否相等。这个断言过去了。但为了简单起见,我想使用CollectionAssert.AreEqual,这样我就不必按照正确的顺序创建预期的对象,所以我开始尝试。使用完全相同的代码,CollectionAssert.AreEquivalent失败。我会说这很奇怪,因为等价是一个比平等更宽松的断言,对吗?我收到这个错误:

CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of <MyObject>. The actual collection contains 0 occurrence(s).

我尝试过调试,但是我没有让它调试.Net框架,即使我设置了下载符号文件。所以我只能看到它进入我的自定义Equals函数 - 它返回true - 然后断言失败。两个对象都有两个元素。调用堆栈(按相反顺序):

  • CollectionAssert.AreEquivalent
  • CollectionAssert.AreEquivalent(过载)
  • CollectionAssert.FindMisMatchedElement
  • Generic.Dictionary(Of Object,int).TryGetValue
  • Generic.Dictionary(Of Object,int).FindEntry
  • Generic.ObjectEqualityComparer.Equals
  • 我的自定义等于

现在我正在写这个,一个想法出现了,我发现了一个潜在的问题。我看到它在内部使用了一个字典。哪个可能作为某种hashmap,其中int是实际列表中的索引?这是否意味着我需要实现自定义IEqualityComparer,而不是覆盖equals?那我的getHashCode()应该怎么样呢? (我猜这是至关重要的,因为我认为它可能用于字典中的密钥?)

1 个答案:

答案 0 :(得分:2)

您走在正确的轨道上:问题确实是您在覆盖GetHashCode时未覆盖Equals

以下是重现问题的示例:

void Main()
{
    var a = new []{new Broken{Foo="a"}, new Broken{Foo="b"}};
    var b = new []{new Broken{Foo="a"}, new Broken{Foo="b"}};

    CollectionAssert.AreEqual(a, b);
    CollectionAssert.AreEquivalent(a, b);
}

class Broken
{
    public string Foo {get;set;}

    public override bool Equals(object obj)
    {
        return Foo == ((Broken)obj).Foo;
    }
}

正如您已正确指出的那样,CollectionAssert.AreEquivalent使用Dictionary,它用于计算每个唯一元素在集合中的频率。

问题不在于哈希码可能会发生冲突,但是如果Equals返回的哈希码不同,那么如果两个应该被视为相等的元素实际上从未使用GetHashCode进行比较。


您可能也会对这个问题感兴趣:

  

Why is it important to override GetHashCode when Equals method is overridden?


  

另外,我的equals函数中有一些逻辑,它不直接比较所有字符串1对1,所以这意味着我基本上会实现这个功能两次,以确保相等的对象获得相同的哈希码,对吧?

不一定。 Dictionary的性能取决于散列算法(当用作密钥时,对象的散列值也不应更改)。

如果您可以忍受一些性能损失(可能忽略不计),您可以使用更简单的方法来计算哈希值,而不是在Equals方法中使用(并接受更多哈希冲突)。如果两个对象的哈希值相等,则无论如何都会调用Equals。 (事实上​​,你可以随时返回相同的值,例如每次1)。


文件中的相关部分:

  

Object.GetHashCode

     

在Hashtable对象中用作键的对象还必须覆盖GetHashCode方法,因为这些对象必须生成自己的哈希码

     

如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较不相等,则两个对象的GetHashCode方法不必返回不同的值。