Search code examples
c#assertxunitxunit.netxunit2

Comparing two List<T> with the XUnit Assert function


The following is not asserting to true with XUnit (StartDate and EndDate are the only two public properties of DatePeriod):

var actual = new List<DatePeriod>()
{
    new DatePeriod() { StartDate = new DateTime(2017, 1, 20), EndDate = new DateTime(2018, 1, 19)},
    new DatePeriod() { StartDate = new DateTime(2018, 1, 20), EndDate = new DateTime(2018, 3, 31)}
};

var expected = new List<DatePeriod>()
{
    new DatePeriod() { StartDate = new DateTime(2017, 1, 20), EndDate = new DateTime(2018, 1, 19)},
    new DatePeriod() { StartDate = new DateTime(2018, 1, 20), EndDate = new DateTime(2018, 3, 31)}
};

Assert.Equal(actual, expected);

Based on some research I expected in the latest version of XUnit that these would end up being considered equal since when using Assert as long as the order is the same which it is.


Solution

  • You simply need to override Equals and GetHashCode like this:

    public class DatePeriod
    {
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != this.GetType()) return false;
    
            DatePeriod other = (DatePeriod)obj;
    
            return StartDate.Equals(other.StartDate) && EndDate.Equals(other.EndDate);
        }
    
        public override int GetHashCode()
        {
            return new {StartDate, EndDate}.GetHashCode();
        }
    
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
    }
    

    xUnit recognizes collections in the sense that you can call Assert.Equal while other testing frameworks require special methods like CollectionAssert.AreEqual.

    In all cases, the framework would invoke Equals for each item in the list passing the corresponding item from the other list. If you have a list of strings or integers then Equals is correctly implemented by default. For custom objects like DatePeriod, the default implementation of the Equals method is based on reference equality, i.e., two objects are equal is they are actually the same object. To get value based equality, you have to override the Equals method (and also GetHashCode method as recommended).