2012-05-24 2 views
14

У меня есть класс PagedModel, который реализует IEnumerable, чтобы просто вернуть ModelData, игнорируя данные подкачки. Я также переопределил Equals и GetHashCode, чтобы сравнить два объекта PagedModel по их параметрам ModelData, PageNumber и TotalPages и PageSize.Assert.AreEqual не использует мои переопределения .Equals в реализации IEnumerable.

Вот проблема

Dim p1 As New PagedModel() With { 
    .PageNumber = 1, 
    .PageSize = 10, 
    .TotalPages = 10, 
    .ModelData = GetModelData() 
} 

Dim p2 As New PagedModel() With { 
    .PageNumber = 1, 
    .PageSize = 10, 
    .TotalPages = 10, 
    .ModelData = GetModelData() 
} 

p1.Equals(p2) =====> True 
Assert.AreEqual(p1, p2) ======> False! 

Похоже, NUnit называет это внутренний EnumerableEqual способ сравнить мой PagedModel вместо того, чтобы использовать Равно методы, которые я обеспечен! Есть ли способ переопределить это поведение или мне нужно написать настраиваемое утверждение.

ответ

9

Выполнение запросов: Я бы посоветовал вам это сделать, но если вам действительно не нравится поведение NUnit и вы хотите настроить утверждение, вы можете предоставить свой собственный EqualityComparer.

Assert.That(p1, Is.EqualTo(p2).Using(myCustomEqualityComparer)); 

Что вы должны делать (короткий ответ): Вам нужно GetHashCode и равняется на ModelData вместо PagedModel, так как вы используете PagedModel как сбор и ModelData как элементы.

Что вы должны делать (длинный ответ): Вместо перекрывая Equals(object) на PagedModel вам нужно реализовать IEquatable<T> на ModelData, где Т является параметром типа к IEnumerable, а также переопределение GetHashCode(). Эти два метода - это то, что все методы IEnumerable в .Net используют для определения равенства (для операций, таких как Union, Distinct и т. Д.) При использовании Default Equality Comparer (вы не укажете свой собственный IEqualityComparer).

код [По умолчанию Равенство Comparer] проверяет, реализует ли тип Т в System.IEquatable интерфейс и, если да, то возвращает EqualityComparer, который использует эту реализацию. В противном случае, она возвращает EqualityComparer, который использует переопределения Object.equals и Object.GetHashCode предоставленных Т.


Для правильной работы GetHashCode должен возвращать те же результаты для всех объектов, которые возвращают верно для .equals (Т). Обратное не обязательно верно - GetHashCode может возвращать столкновения для объектов, которые не равны. More information here - see Marc Gravel's accepted answer. Я также нашел реализацию GetHashCode в этом ответе, используя простые значения.

+0

Этот ответ заключается в том, что по существу в вашей реализации вам необходимо явно реализовать IEquatable .Equals ... см. Http://stackoverflow.com/questions/1577149/explicit-interface-implementation-in-vb-net – Jay

+0

Нет, реализация IEquatable не достаточно. Реализация GetHashCode так же важна. Также важно понять, что IEnumerable возвращается к использованию Equals (object), если вы не реализуете IEquatable (см. Цитату в моем сообщении), поэтому делать это не обязательно. – csauve

+0

Я сказал, что явным образом реализую IEquatable.Equals, который не должен позволять отмену по умолчанию, потому что метод будет переопределен, и явная реализация вызовет указанный метод ... – Jay

1

Если вы посмотрите на реализации равенства компаратора NUnit в GIT repo, вы увидите, что есть специальный блок сравнения для двух перечислений, который имеет более высокий приоритет (просто потому, что она находится выше), чем comparisons, используя интерфейс IEquatable<T> или метод Object.Equals(Object), который вы внедрили или перегрузили в своем классе PagedModel.

Я не знаю, если это ошибка или особенность, но вы, вероятно, должны спросить себя, во-первых, если реализующий интерфейс IEnumerable<ModelData> непосредственно вашим PagedModel класса на самом деле лучший вариант, особенно потому, что ваш PagedModel это нечто большее, чем просто перечисление ModelData экземпляров.

Вероятно, было бы достаточно (или даже лучше) предоставить перечисление ModelData через простое свойство IEnumerable<ModelData> только для чтения класса PagedModel. NUnit перестанет смотреть на ваш объект PagedModel, как при простом перечислении объектов ModelData, и ваши модульные тесты будут вести себя так, как ожидалось.

Единственный другой вариант, предложенный csauve; реализовать простой пользовательский IComparer для PagedModel и предоставить экземпляр этого всем утверждает, где вы будете сравнивать два PagedModel экземпляра:

internal class PagedModelComparer : System.Collections.IComparer 
{ 
    public static readonly IComparer Instance = new PagedModelComparer(); 

    private PagedModelComparer() 
    { 
    } 

    public int Compare(object x, object y) 
    { 
     return x is PagedModel && ((PagedModel)x).Equals(y); 
    } 
} 

    ... 
    [Test] 
    ... 
     Assert.That(actual, Is.EqualTo(expected).Using(PagedModelComparer.Instance)); 
    ... 

Но это будет сделать тесты более сложными, чем это необходимо, и вы всегда должны подумайте использовать свой специальный компаратор всякий раз, когда вы пишете дополнительные тесты для PagedModel.

Смежные вопросы