问题
I'm trying to compare 2 Lists (wrapped in an object) containing custom objects. I don't care about the order, but if list 1 contains "1,2,3,4" then list 2 must and only contain those elements. E.g.: "4,2,3,1"
Based on Compare two List<T> objects for equality, ignoring order ignoring-order I've used the Except and Any but it doesn't give me the desired results.
If I use Assert.Equals
it fails, but Assert.IsTry(list1.equals(list2))
succeeds.
Further more if I remove the Equals and GetHashCode implementation then both tests fail.
public class AppointmentCollection : List<Appointment>
{
public override bool Equals(object obj)
{
var appCol = obj as AppointmentCollection;
if (appCol == null)
{
return false;
}
return (appCol.Count == this.Count) && !(this.Except(appCol).Any());
}
public override int GetHashCode()
{
unchecked
{
//use 2 primes
int hash = 17;
foreach (var appointment in this)
{
hash = hash * 19 + appointment.GetHashCode();
}
return hash;
}
}
}
public class Appointment
{
public string Title {get; set;}
public DateTime StartTime {get; set;}
public DateTime EndTime { get; set;}
public override bool Equals(object obj)
{
var appointment = obj as Appointment;
if (appointment == null)
{
return false;
}
return Title.Equals(appointment.Title) &&
StartTime.Equals(appointment.StartTime) &&
EndTime.Equals(appointment.EndTime);
}
public override int GetHashCode()
{
unchecked
{
//use 2 primes
int hash = 17;
hash = hash * 19 + Title.GetHashCode();
hash = hash * 19 + StartTime.GetHashCode();
hash = hash * 19 + EndTime.GetHashCode();
return hash;
}
}
}
[Test]
public void TestAppointmentListComparisonDifferentOrder()
{
var appointment1 = new Appointment(
"equals test1",
new DateTime(2013, 9, 4),
new DateTime(2013, 9, 4));
var appointment2 = new Appointment(
"equals test2",
new DateTime(2013, 9, 4),
new DateTime(2013, 9, 4));
var list1 = new AppointmentCollection() { appointment1, appointment2 };
var list2 = new AppointmentCollection() { appointment2, appointment1 };
//With Equals/GetHashCode in AppointmentCollection implemented
CollectionAssert.AreEqual(list1, list2); //fails
Assert.IsTrue(list1.Equals(list2)); //success
//Without Equals/GetHashCode in AppointmentCollection implemented
CollectionAssert.AreEqual(list1, list2); //fails
Assert.IsTrue(list1.Equals(list2)); //fails
}
回答1:
You didn't state clearly which unit test tool you use. Maybe CollectionAssert
is the class Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert, or maybe it is NUnit.Framework.CollectionAssert, or maybe something else?
So check the documentation of your testing tool, or write here which one you use.
However, it is common for
CollectionAssert.AreEqual( ... );
to check if the collections are the same in the same order, while
CollectionAssert.AreEquivalent( ... );
will check what you want. So use the latter.
Neither of the two methods on CollectionAssert
actually uses your override of Equals(object)
. To use that, write:
Assert.AreEqual( ... );
Edit: I thought Assert.AreEqual(exp, act);
would always end up doing exp.Equals(act)
which would call your override on AppointmentCollection
. But it turns out we end in the private instance method EqualConstraint.ObjectsEqual, and as one sees it checks if the run-time type implements ICollection
in which case your override is never used.
Lesson learned: Using Assert.AreEqual
can be confusing with collections. Use CollectionAssert.AreEquivalent
or CollectionAssert.AreEqual
to make your intention clear. You don't have to override Equals
on AppointmentCollection
if you only need it for testing. If you need it for the application itself and you want to test that, write the test with list1.Equals(list2)
literally to make sure your own override is what is tested.
(In any case the override on Appointment
is needed, of course.)
来源:https://stackoverflow.com/questions/18632069/c-sharp-compare-lists-with-custom-object-but-ignore-order