MSTest: CollectionAssert.AreEquivalent failed. The expected collection contains 1 occurrence(s) of
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.
Related videos on Youtube
Lernkurve
Updated on January 09, 2020Comments
-
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
andlist2
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 about 13 yearsThanks 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 over 12 yearsWhy does CollectionAssert.AreEquivalent fail? What if I WANT to use AreEquivalent because order is not important?
-
neontapir over 11 yearsInstead of
!Any
, you could useAll
. -
R. Schreurs about 11 yearsI 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 about 11 yearsWhy 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 about 11 yearsIf 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 about 11 yearsIndeed. I personally used this to test for rather large and complex objects, so that case was never an issue.
-
neontapir about 10 yearsOne possible explanation is that
CollectionAssert
may use its own semantics to compare collections, rather than usingIEqualityComparer<T>
. Take a look at your unit test framework's documentation. You can determine this by writing your ownCollectionAssert.AreEqual
implementation to see if the problem is with the comparer or the testing framework method. -
BenKoshy over 7 yearsfor 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 over 7 yearsThis should be in the NUnit documentation!
-
kuldeep over 6 yearswhat happens when MyPerson has another list of custom Objects and in this case how to provide comparison/equality ?
-
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
ussesGetHasCode()
andEquals()
to test for equivalency. -
Squirrel in training almost 4 years@BramVandenbussche It's because
CollectionAssert
not only checks for Equality with.Equals
but also with.GetHashCode
-
neontapir almost 4 yearsInteresting. That may be true today under newer versions of the framework. It wasn't required in 2011.