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

Следующее не утверждает истину с 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, пока порядок остается таким же, как и он.


person Blake Rivell    schedule 28.03.2016    source источник
comment
Определены ли методы Equals и GetHashCode в Period?   -  person Yacoub Massad    schedule 28.03.2016
comment
Нет, это то, что мне нужно сделать?   -  person Blake Rivell    schedule 28.03.2016
comment
@YacoubMassad, пожалуйста, посмотрите мой комментарий к вашему ответу, мне пришлось немного обновить свой пост, так как нет объекта Period, все просто использует DatePeriod.   -  person Blake Rivell    schedule 28.03.2016


Ответы (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, как рекомендуется).

person Yacoub Massad    schedule 28.03.2016
comment
Эй, похоже, это именно то, что мне нужно, но можете ли вы взглянуть на недавнее обновление, которое я сделал для своего сообщения. Прошу прощения, потому что такого объекта, как Period, нет. Все использует DatePeriod. - person Blake Rivell; 28.03.2016
comment
@BlakeRivell Изначально у вас был List ‹DatePeriod› с рядом периодов внутри него, что заставило Якуба поверить, что Period является подклассом DatePeriod. - person Frozenthia; 28.03.2016
comment
@TheAnathema Correct, поэтому я попытался сообщить ему о своей ошибке в комментариях и спросил, может ли он обновить свой ответ. - person Blake Rivell; 28.03.2016
comment
Это так странно, как в этом посте говорится, что Xunit распознает коллекции, поэтому в этом нет необходимости ... stackoverflow.com/questions/419659/. Что я не понимаю? - person Blake Rivell; 28.03.2016
comment
Должен ли я реализовывать IEquatable или IEqualityComparer? Похоже, Якуб говорит мне реализовать IEqualityComparer. - person Blake Rivell; 28.03.2016
comment
Yacoub Я пытаюсь уведомить вас, что DatePeriod больше не является сложным объектом, он плоский. Я обновил свой пост. Не могли бы вы исправить свой ответ? - person Blake Rivell; 28.03.2016
comment
Для любого настраиваемого объекта вы должны переопределить Equals и GetHashCode. Эти методы определены в классе object. И мы просто отменяем их в DatePeriod. - person Yacoub Massad; 28.03.2016
comment
Якуб благодарит вас за дополнительное объяснение, однако вы все еще используете точку в своем ответе. Срок не существует. Если вы исправите это, я отмечу как правильное. Пожалуйста, посмотрите мой обновленный пост, у меня изначально была опечатка. - person Blake Rivell; 28.03.2016
comment
Я забыл изменить эту строчку. Обновлено сейчас. - person Yacoub Massad; 28.03.2016
comment
Прекрасно, теперь я это вижу. Мой последний вопрос: почему вы не реализуете IEqualityComparer? - person Blake Rivell; 28.03.2016
comment
Зачем мне это нужно? Обычно вы делаете это, если хотите создать настраиваемый компаратор, который может сравнивать два экземпляра чего-либо, и по какой-то причине вы не хотите помещать такую ​​логику сравнения в сам объект (тот, который вы хотите сравнить). - person Yacoub Massad; 28.03.2016
comment
Когда вы по какой-то причине говорите, что не хотите помещать такую ​​логику сравнения в сам объект, вы имеете в виду меня и то, что я сказал? Я просто пытаюсь подтвердить, что ваш окончательный ответ - это именно то, что мне нужно, поскольку у меня есть два списка плоского объекта с именем DatePeriod, которые я хочу сравнить. Есть ли другой способ сделать это? - person Blake Rivell; 28.03.2016
comment
Нет, иногда я использую его, потому что у меня есть собственная логика сравнения, которая не присуща самому объекту. Например, в некоторых случаях я хочу сравнивать только ID, а не остальные свойства. Например, когда вы создаете Dictionary, вы можете передать такой настраиваемый компаратор. - person Yacoub Massad; 28.03.2016
comment
Если есть стандартный способ сравнения объекта, то вы должны поместить такую ​​логику сравнения на сам объект. Если логика сравнения настраивается, вы помещаете ее в настраиваемый объект (который реализует IEqualityComparer). - person Yacoub Massad; 28.03.2016

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;
     }
}

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

person Frozenthia    schedule 28.03.2016