-
Notifications
You must be signed in to change notification settings - Fork 181
Description
Hi,
First of all, thanks for a great testing framework. We use it extensively at work and are generally very pleased with it.
However, we've run recently into a weird situation where equality tests seem to be failing when they shouldn't. It's easier to explain with an example.
Consider a Person class, which implements both IEquatable<T> and IComparable<T>:
- 2 persons are equal if they have the same ID
- the order between 2 persons is defined by their name (alphabetical). So a sorted list of 4 persons might look like
[ Amy, Ben, Ben, Beth](with no preference for whichBencomes first).
class Person : IEquatable<Person>, IComparable<Person>
{
private readonly int _id;
private readonly string _name;
public Person(int id, string name)
{
_id = id;
_name = name;
}
public bool Equals(Person other)
{
return (other != null) && other._id == this._id;
}
public override int GetHashCode()
{
return _id;
}
public int CompareTo(Person other)
{
return this._name.CompareTo(other._name);
}
}And the following test:
class Test
{
static Person person1, person2;
Establish context = () =>
{
person1 = new Person(1, "John Doe");
person2 = new Person(2, "John Doe"); // Same name, different person
};
It should_have_the_same_rank = () => person1.CompareTo(person2).ShouldEqual(0); // assertion passes
It should_not_be_the_same_person = () => person1.ShouldEqual(person2); // assertion passes (but should fail?)
It should_be_2_different_persons_v1 = () => person1.Equals(person2).ShouldBeFalse(); // assertion passes
It should_be_2_different_persons_v2 = () => person1.ShouldNotEqual(person2); // assertion fails (but should pass?)
}For some reason, 2 of these assertions fail. We had a quick look at why that might be and it seems that MSpec uses a number of Comparer strategies, as defined here.
I could be wrong, but do these mean that MSpec gives IComparable<T> preference over IEquatable<T>? If so, that seems to me like a misunderstanding. I always thought IComparable was about ordering things and IEquatable was about defining what makes 2 things equal. So I think I would expect:
- the
EquatableComparerto come first in the list - and to be the only comparer used for cases which do implementIEquatable<T>. If I have explicitly defined the meaning of equality for a type, I'd expect this to be the only definition of equality. IComparable<T>to not feature in the list, since it is about comparison, not about equality. To me that's a different use case (at least that 's how I read the docs:IComparableis about ordering and sorting,IEquatableis about equality). For instance, in the example above, 2 persons can have the same name, and therefore the same order, but that doesn't mean they are the same person.
Am I missing something? Is there an underlying reason for using IComparable<T> when determining equality? What is the recommended way to test equality for objects which implement IComparable<T>?
We also found this old issue, which is somewhat related.
Thanks for your help! :)