【问题标题】:MSTest: CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) ofMSTest:CollectionAssert.AreEquivalent 失败。预期的集合包含 1 次出现
【发布时间】:2011-07-08 20:58:54
【问题描述】:

问题

谁能告诉我为什么我的单元测试失败并显示此错误消息?

CollectionAssert.AreEquivalent 失败。预期的集合包含 1 的发生。实际上 集合包含 0 次出现。

目标

我想检查两个列表是否相同。如果两者都包含具有相同属性值的相同元素,则它们是相同的。顺序无关紧要。

代码示例

这是产生错误的代码。 list1list2 是相同的,即相互复制粘贴。

[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());

附言

相关堆栈溢出问题:

这两个问题我都看过,但答案都没有帮助。

【问题讨论】:

    标签: c# .net unit-testing mstest


    【解决方案1】:

    如果我按照MSDN 的描述添加IEqualityComparer&lt;T&gt; 并且如果我使用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;
        }
    }
    

    【讨论】:

    • 为什么 CollectionAssert.AreEquivalent 会失败?如果我想使用 AreEquivalent 因为顺序不重要怎么办?
    • 为什么 CollectionAssert.AreEquivalent(list1, list2);不起作用,如果我在 MyPerson 上实现接口 IEqualityComparer?我试过了,但不能让它工作。
    • 一种可能的解释是CollectionAssert 可能使用它自己的语义来比较集合,而不是使用IEqualityComparer&lt;T&gt;。查看您的单元测试框架的文档。您可以通过编写自己的CollectionAssert.AreEqual 实现来确定这一点,以查看问题出在比较器还是测试框架方法上。
    • 这应该在 NUnit 文档中!
    • 当 MyPerson 有另一个自定义对象列表时会发生什么,在这种情况下如何提供比较/相等?
    【解决方案2】:

    你完全正确。除非您提供类似IEqualityComparer&lt;MyPerson&gt; 或实现MyPerson.Equals(),否则两个MyPerson 对象将与object.Equals 进行比较,就像任何其他对象一样。由于对象不同,Assert 会失败。

    【讨论】:

    • 感谢您的回答!我会将您的答案标记为已接受的答案,因为它解释了“为什么”。我将链接到我的答案以显示the "how"
    • 我从@Lernkurve 的答案开始,效果很好。之后,我尝试将方法 Equals 和 GetHashCode 移至 MyPerson 类,使其也实现 IEqualityComparer。我希望 Assert.IsTrue(list1.SequenceEqual(list2));现在将在 MyPerson 上使用 IEqualityComparer 实现,但它没有。我得到“Assert.IsTrue 失败”。因此,您的建议“......或实施 MyPerson.Equals()”似乎不起作用。我不明白为什么。
    • 对于那些面临与@R.Schreurs 相同问题的人,我认为解决方案是将比较器传递给测试断言本身。
    • @R.Schreurs @neontapir 我已经在类本身上覆盖了 .Equals() 并且它不起作用。所以我决定也覆盖 .GetHashCode() 并且成功了。似乎CollectionAssert.AreEquivalent 使用GetHasCode()Equals() 来测试等效性。
    • 有趣。在较新版本的框架下,今天这可能是真的。 2011 年不需要。
    【解决方案3】:

    我写这个是为了测试顺序不重要的集合:

        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&lt;MyType&gt;(collectionA, collectionB, comparer));

    【讨论】:

    • 你可以使用!Any,而不是All
    • 如果项目的多样性是相关的,那么您的测试就有错误。它将在 "AreCollectionsEquivalent(new List() { 1, 1, 2 }, new List() { 1, 2, 2 }, EqualityComparer.Default);"上返回 true >
    • 确实如此。我个人用它来测试相当大和复杂的对象,所以这种情况从来都不是问题。
    【解决方案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

    【讨论】:

      【解决方案5】:

      如果您想在不编写相等比较器的情况下实现此目的,可以使用一个单元测试库,称为 FluentAssertions

      https://fluentassertions.com/documentation/

      它有许多内置的相等扩展函数,包括用于集合的函数。您可以通过 Nuget 安装它,并且非常易于使用。

      以上面问题中的例子,你最后要写的是

      list1.Should().BeEquivalentTo(list2);
      

      默认情况下,两个集合中的顺序很重要,但也可以更改。

      【讨论】:

        猜你喜欢
        • 2015-09-15
        • 2013-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多