问题:
任何人都可以告诉我为什么我的单元测试失败并出现此错误消息吗?
CollectionAssert.AreEquivalent失败。预期的集合包含1 发生的。实际上 集合包含0次出现。
目标:
我想检查两个列表是否相同。如果它们包含具有相同属性值的相同元素,则它们是相同的。订单无关紧要。
代码示例:
这是产生错误的代码。 list1
和list2
相同,即彼此的复制粘贴。
[TestMethod]
public void TestListOfT()
{
var list1 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
var list2 = new List<MyPerson>()
{
new MyPerson()
{
Name = "A",
Age = 20
},
new MyPerson()
{
Name = "B",
Age = 30
}
};
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
}
public class MyPerson
{
public string Name { get; set; }
public int Age { get; set; }
}
我也试过这一行(source)
CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
和这一行(source)
CollectionAssert.AreEquivalent(list1.ToArray(), list2.ToArray());
P.S。
相关的Stack Overflow问题:
我已经看到了这两个问题,但答案没有帮助。
答案 0 :(得分:22)
你是对的。除非您提供类似IEqualityComparer<MyPerson>
或实施MyPerson.Equals()
的内容,否则两个MyPerson
对象将与object.Equals
进行比较,就像任何其他对象一样。由于对象不同,Assert将失败。
答案 1 :(得分:21)
如果我按MSDN所述添加IEqualityComparer<T>
并使用Enumerable.SequenceEqual
,则此功能正常。但请注意,现在元素的顺序是相关的。
在单元测试中
//CollectionAssert.AreEquivalent(list1, list2); // Does not work
Assert.IsTrue(list1.SequenceEqual(list2, new MyPersonEqualityComparer())); // Works
<强>的IEqualityComparer 强>
public class MyPersonEqualityComparer : IEqualityComparer<MyPerson>
{
public bool Equals(MyPerson x, MyPerson y)
{
if (object.ReferenceEquals(x, y)) return true;
if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null)) return false;
return x.Name == y.Name && x.Age == y.Age;
}
public int GetHashCode(MyPerson obj)
{
if (object.ReferenceEquals(obj, null)) return 0;
int hashCodeName = obj.Name == null ? 0 : obj.Name.GetHashCode();
int hasCodeAge = obj.Age.GetHashCode();
return hashCodeName ^ hasCodeAge;
}
}
答案 2 :(得分:4)
在测试nHibernate持久化的集合时,我遇到了同样的错误。我能够通过覆盖 Equals和GetHashCode 方法来实现这一点。如果我没有覆盖两者,我仍然会遇到你提到的同样的错误:
CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of .
The actual collection contains 0 occurrence(s).
我有以下对象:
public class EVProjectLedger
{
public virtual long Id { get; protected set; }
public virtual string ProjId { get; set; }
public virtual string Ledger { get; set; }
public virtual AccountRule AccountRule { get; set; }
public virtual int AccountLength { get; set; }
public virtual string AccountSubstrMethod { get; set; }
private Iesi.Collections.Generic.ISet<Contract> myContracts = new HashedSet<Contract>();
public virtual Iesi.Collections.Generic.ISet<Contract> Contracts
{
get { return myContracts; }
set { myContracts = value; }
}
public override bool Equals(object obj)
{
EVProjectLedger evProjectLedger = (EVProjectLedger)obj;
return ProjId == evProjectLedger.ProjId && Ledger == evProjectLedger.Ledger;
}
public override int GetHashCode()
{
return new { ProjId, Ledger }.GetHashCode();
}
}
我使用以下方法测试过:
using (ITransaction tx = session.BeginTransaction())
{
var evProject = session.Get<EVProject>("C0G");
CollectionAssert.AreEquivalent(TestData._evProjectLedgers.ToList(), evProject.EVProjectLedgers.ToList());
tx.Commit();
}
我正在使用nHibernate,它鼓励覆盖这些方法。我可以看到的一个缺点是我的Equals方法基于对象的业务键,因此使用业务键测试相等性而没有其他字段。您可以根据需要覆盖Equals,但要注意本文中提到的平等污染:
CollectionAssert.AreEquivalent failing... can't figure out why
答案 3 :(得分:0)
我写这个来测试订单不重要的集合:
public static bool AreCollectionsEquivalent<T>(ICollection<T> collectionA, ICollection<T> collectionB, IEqualityComparer<T> comparer)
{
if (collectionA.Count != collectionB.Count)
return false;
foreach (var a in collectionA)
{
if (!collectionB.Any(b => comparer.Equals(a, b)))
return false;
}
return true;
}
不如使用SequenceEquals
那么优雅,但它有效。
当然要使用它你只需:
Assert.IsTrue(AreCollectionsEquivalent<MyType>(collectionA, collectionB, comparer));
答案 4 :(得分:0)
如果您希望不必编写相等的比较器就可以实现这一目标,那么可以使用一个名为 FluentAssertions
的单元测试库。https://fluentassertions.com/documentation/
它具有许多内置的相等性扩展功能,包括用于Collection的功能。您可以通过Nuget来安装它,它真的很容易使用。
以上述问题中的示例为基础,最后您必须写的是
list1.Should().BeEquivalentTo(list2);
默认情况下,顺序在两个集合中很重要,但是也可以更改。