MSTest: CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of

15,375

Solution 1

You are absolutely right. Unless you provide something like an IEqualityComparer<MyPerson> or implement MyPerson.Equals(), the two MyPerson objects will be compared with object.Equals, just like any other object. Since the objects are different, the Assert will fail.

Solution 2

It works if I add an IEqualityComparer<T> as described on MSDN and if I use Enumerable.SequenceEqual. Note however, that now the order of the elements is relevant.

In the unit test

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

Solution 3

I was getting this same error when testing a collection persisted by nHibernate. I was able to get this to work by overriding both the Equals and GetHashCode methods. If I didn't override both I still got the same error you mentioned:

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

I had the following object:

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

Which I tested using the following:

using (ITransaction tx = session.BeginTransaction())
{
    var evProject = session.Get<EVProject>("C0G");

    CollectionAssert.AreEquivalent(TestData._evProjectLedgers.ToList(), evProject.EVProjectLedgers.ToList());

    tx.Commit();
}

I'm using nHibernate which encourages overriding these methods anyways. The one drawback I can see is that my Equals method is based on the business key of the object and therefore tests equality using the business key and no other fields. You could override Equals however you want but beware of equality pollution mentioned in this post:

CollectionAssert.AreEquivalent failing... can't figure out why

Solution 4

If you would like to achieve this without having to write an equality comaparer, there is a unit testing library that you can use, called FluentAssertions,

https://fluentassertions.com/documentation/

It has many built in equality extension functions including ones for the Collections. You can install it through Nuget and its really easy to use.

Taking the example in the question above all you have to write in the end is

list1.Should().BeEquivalentTo(list2);

By default, the order matters in the two collections, however it can be changed as well.

Share:
15,375

Related videos on Youtube

Lernkurve
Author by

Lernkurve

Updated on January 09, 2020

Comments

  • Lernkurve
    Lernkurve over 4 years

    Question:

    Can anyone tell me why my unit test is failing with this error message?

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

    Goal:

    I'd like to check if two lists are identical. They are identical if both contain the same elements with the same property values. The order is irrelevant.

    Code example:

    This is the code which produces the error. list1 and list2 are identical, i.e. a copy-paste of each other.

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

    I've also tried this line (source)

    CollectionAssert.AreEquivalent(list1.ToList(), list2.ToList());
    

    and this line (source)

    CollectionAssert.AreEquivalent(list1.ToArray(), list2.ToArray());
    

    P.S.

    Related Stack Overflow questions:

    I've seen both these questions, but the answers didn't help.

  • Lernkurve
    Lernkurve about 13 years
    Thanks for your answer! I'll mark your answer as the accepted answer since it explains the "why". And I'll link to my answer to show the "how".
  • Bram Vandenbussche
    Bram Vandenbussche over 12 years
    Why does CollectionAssert.AreEquivalent fail? What if I WANT to use AreEquivalent because order is not important?
  • neontapir
    neontapir over 11 years
    Instead of !Any, you could use All.
  • R. Schreurs
    R. Schreurs about 11 years
    I started with the answer of @Lernkurve, and it worked well. After that, I tried to move the methods Equals and GetHashCode to the MyPerson class, making it implement IEqualityComparer<MyPerson> as well. I would expect that Assert.IsTrue(list1.SequenceEqual(list2)); would now use the IEqualityComparer implementation on MyPerson, but it does not. I get "Assert.IsTrue failed". So, your suggestion "... or implement MyPerson.Equals()" does not seem to work. I do not understand why.
  • R. Schreurs
    R. Schreurs about 11 years
    Why does CollectionAssert.AreEquivalent(list1, list2); not work, if I implement the interface IEqualityComparer<MyPerson> on MyPerson? I tried, but cannot get it to work.
  • R. Schreurs
    R. Schreurs about 11 years
    If the multiplicity of items is of relevance, your test has a bug. It will return true on "AreCollectionsEquivalent(new List<int>() { 1, 1, 2 }, new List<int>() { 1, 2, 2 }, EqualityComparer<int>.Default);"
  • Shaamaan
    Shaamaan about 11 years
    Indeed. I personally used this to test for rather large and complex objects, so that case was never an issue.
  • neontapir
    neontapir about 10 years
    One possible explanation is that CollectionAssert may use its own semantics to compare collections, rather than using IEqualityComparer<T>. Take a look at your unit test framework's documentation. You can determine this by writing your own CollectionAssert.AreEqual implementation to see if the problem is with the comparer or the testing framework method.
  • BenKoshy
    BenKoshy over 7 years
    for those facing the same issue as @R.Schreurs i think the solution would be to pass the comparer into the test assertion itself.
  • Chaim Eliyah
    Chaim Eliyah over 7 years
    This should be in the NUnit documentation!
  • kuldeep
    kuldeep over 6 years
    what happens when MyPerson has another list of custom Objects and in this case how to provide comparison/equality ?
  • Squirrel in training
    Squirrel in training almost 4 years
    @R.Schreurs @neontapir I've overriden .Equals() on the class itself aswell and it doesn't work. So I decided to override .GetHashCode() aswell and that did the trick. It seems to be that CollectionAssert.AreEquivalent usses GetHasCode() and Equals() to test for equivalency.
  • Squirrel in training
    Squirrel in training almost 4 years
    @BramVandenbussche It's because CollectionAssert not only checks for Equality with .Equals but also with .GetHashCode
  • neontapir
    neontapir almost 4 years
    Interesting. That may be true today under newer versions of the framework. It wasn't required in 2011.