Сравнение двух List<T> с функцией XUnit Assert

Следующее не утверждает true с XUnit (StartDate и EndDate являются единственными двумя открытыми свойствами 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);

Основываясь на некоторых исследованиях, я ожидал, что в последней версии XUnit они в конечном итоге будут считаться равными, поскольку при использовании Assert, если порядок такой же, как и у него.

2 ответа

Решение

Вам просто нужно переопределить Equals а также GetHashCode как это:

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 распознает коллекции в том смысле, что вы можете вызвать Assert.Equal в то время как другие платформы тестирования требуют специальных методов, таких как CollectionAssert.AreEqual,

Во всех случаях структура будет вызывать Equals для каждого элемента в списке, передавая соответствующий элемент из другого списка. Если у вас есть список строк или целых чисел, то Equals правильно реализовано по умолчанию. Для пользовательских объектов, таких как DatePeriodреализация по умолчанию Equals Метод основан на ссылочном равенстве, т. е. два объекта равны, если они фактически являются одним и тем же объектом. Чтобы получить равенство на основе значений, вы должны переопределить Equals метод (а также GetHashCode метод в соответствии с рекомендациями).

List<T> равенство не проверяется элемент за элементом.

Используйте метод LINQ SequenceEqual, чтобы проверить ваше равенство.

var equal = actual.SequenceEqual(expected);

И реализовать IEquatable на вашем объекте:

public class DatePeriod : IEquatable<DatePeriod>
{
     public DateTime StartDate { get; set; }
     public DateTime EndDate { get; set; }

     public bool Equals(Period other)
     {
         return StartDate == other.StartDate && EndDate == other.EndDate;
     }
}

Для безопасности проверьте на нули, а что нет. Смотрите ответ Якуба для более полной реализации.

Другие вопросы по тегам