I'm using xUnit and it doesn't have a way to determine if 2 IEnumerable<T>
are equal if T
is custom type.
I've tried using LINQ SequenceEqual
but again as the instances of T
are different this returns false;
Here is a basic test with a non-working IEqualityComparer
[Fact]
public void FactMethodName()
{
var one = new[] { new KeywordSchedule() { Id = 1 } };
var two = new[] { new KeywordSchedule() { Id = 1 } };
Assert.Equal(one, two, new KeywordScheduleComparer());
}
public class KeywordScheduleComparer : IEqualityComparer<IEnumerable<KeywordSchedule>>
{
public bool Equals(IEnumerable<KeywordSchedule> x, IEnumerable<KeywordSchedule> y)
{
return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y));
}
public int GetHashCode(IEnumerable<KeywordSchedule> obj)
{
if (obj == null)
return 0;
return unchecked(obj.Select(e => e.GetHashCode()).Aggregate(0, (a, b) => a + b)); // BAD
}
}
I'm using this in an integration test, so I insert data from a IEnumerable into a DB at the start, then call my SUT to retrieve data from DB and compare.
If you can help me get a collection comparison working I'd appreciate it!
I just verified that this works fine with xUnit.net 1.9.2:
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
}
public class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass x, MyClass y)
{
return x.ID == y.ID;
}
public int GetHashCode(MyClass obj)
{
return obj.ID.GetHashCode();
}
}
public class ExampleTest
{
[Fact]
public void TestForEquality()
{
var obj1 = new MyClass { ID = 42, Name = "Brad" };
var obj2 = new MyClass { ID = 42, Name = "Joe" };
Assert.Equal(new[] { obj1 }, new[] { obj2 }, new MyClassComparer());
}
}
So I'm not 100% clear why you need the extra comparer. Just the single comparer should be sufficient.
Well, your implementation is pending. You implemented custom comparer for IEnumerable<KeywordSchedule>
but forgot to implement the same for KeywordSchedule
.
x.SequenceEqual
Still uses Comparer<T>.Default
so it goes for reference comaprison and hence result is false.
public class KScheduleComparer : IEqualityComparer<KeywordSchedule>
{
public bool Equals(KeywordSchedule x, KeywordSchedule y)
{
return x.Id == y.Id;
}
public int GetHashCode(KeywordSchedule obj)
{
return obj.GetHashCode();
}
}
Then modify your Equals method in KeywordScheduleComparer
class as below
public class KeywordScheduleComparer : IEqualityComparer<IEnumerable<KeywordSchedule>>
{
public bool Equals(IEnumerable<KeywordSchedule> x, IEnumerable<KeywordSchedule> y)
{
return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y, new KScheduleComparer()));
}
public int GetHashCode(IEnumerable<KeywordSchedule> obj)
{
if (obj == null)
return 0;
return unchecked(obj.Select(e => e.GetHashCode()).Aggregate(0, (a, b) => a + b)); // BAD
}
}
You could do this more elegantly using FluentAssertions library. It has plenty assertion methods for collections.
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
protected bool Equals(MyClass other)
{
return ID == other.ID;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((MyClass) obj);
}
public override int GetHashCode()
{
unchecked
{
return (ID*397) ^ (Name != null ? Name.GetHashCode() : 0);
}
}
}
public class ExampleTest
{
[Fact]
public void TestForEquality()
{
var obj1 = new MyClass { ID = 42, Name = "Rock" };
var obj2 = new MyClass { ID = 42, Name = "Paper" };
var obj3 = new MyClass { ID = 42, Name = "Scissors" };
var obj4 = new MyClass { ID = 42, Name = "Lizard" };
var list1 = new List<MyClass> {obj1, obj2};
list1.Should().BeEquivalentTo(obj3, obj4);
}
}