Коллекция Assert содержит объект пользовательского класса, который не переопределяет equals/hashcode
У нас есть пользовательский класс с несколькими полями, для которых мы не можем переопределить методы equals/hashcode по причинам бизнес-домена
Тем не менее, во время модульного тестирования мы должны утверждать, содержит ли коллекция элемент этого класса
List<CustomClass> customObjectList = classUnderTest.methodUnderTest();
//create customObject with fields set to the very same values as one of the elements in customObjectList
//we should assert here that customObjectList contains customObject
Однако до сих пор мы не нашли ни одного решения, которое работало бы без переопределения equals/hashcode, например Hamcrest
assertThat(customObjectList, contains(customObject));
Приводит к AssertionError цитированию
Expected: iterable containing [<CustomClass@578486a3>]
but: item 0: was <CustomClass@551aa95a>
Есть ли решение этой проблемы без необходимости сравнить поле за полем?
6 ответов:
Если вы используете Java8, вы можете использовать Stream#anyMatch и свой собственный метод customEquals. Что-то вроде этого сработает -
assertTrue(customObjectList.stream() .anyMatch(object -> customEquals(object,customObject)));
Обновлено , чтобы отразить комментарий Хольгера
Я хотел бы поблагодарить вас за все ответы, были сделаны некоторые действительно хорошие замечания
Однако я забыл упомянуть в своем вопросе, что наши пользовательские классы рекурсивны, то есть содержат поля других типов пользовательских классов, для которых применяется то же ограничение относительно равенства и переопределения хэш-кода. К сожалению, ни одно из упомянутых готовых решений (AssertJ, Nitor Creations), похоже, не поддерживает глубокое сравнение
Тем не менее, там все еще кажется, что есть решение, и это класс ReflectionAssert от Unitils. Следующее, кажется, работает, как мы и ожидали, даже в состоянии игнорировать порядок элементов в коллекции
assertReflectionEquals(Arrays.asList(customObject1, customObject3, customObject2), customObjectList, ReflectionComparatorMode.LENIENT_ORDER);
Fest утверждения имеет следующее:
assertThat(expected).isEqualsToByComparingFields(actual);
Я думаю, что он делает сравнение отражения под капотом. У нас была аналогичная проблема, и это спасло нас от написания пользовательской логики сравнения.
Другое дело, чтобы расширить рамки утверждения по вашему выбору с чем-то для вашего точного класса и случая. Этот подход должен сбросить некоторые накладные расходы на производительность при использовании сравнения с глубоким отражением.
Нет.
Многие методы интерфейса
Collection
специально определены в терминахequals()
. Например.Collection#contains()
:Возвращает
true
, Если эта коллекция содержит указанный элемент. Больше формально возвращаетtrue
тогда и только тогда, когда эта коллекция содержит at хотя бы один элементe
такой, что(o==null ? e==null : o.equals(e))
.Просто для-каждой коллекции и сравнить поле за полем. Вы можете спрятать логику сравнения / выравнивания в статический служебный класс, например
CustomClasses
или аналогично, а затем написать пользовательский Hamcrest matcher.Альтернативно, используйте отражающие равенства, например, из Apache Commons Lang , расширение Hamcrest, или (предпочтительно) мигрируйте в AssertJ , оно имеет эту функциональность из коробки:
[9] это хитро и изворотливо, но достаточно хорошо для тестов. Пожалуйста, не используйте в производственном коде.assertThat(ImmutableList.of(frodo)) .usingFieldByFieldElementComparator() .contains(frodoClone);
Я знаю два решения ваших проблем, которые используют Hamcrest. Первый тестирует некоторые свойства элемента.
assertThat(customObjectList, contains(allOf( hasProperty("propertyA", equalTo("someValue")), hasProperty("propertyB", equalTo("anotherValue")))));
Или вы можете использовать
reflectEquals
matcher из Nitor Creations :assertThat(customObjectList, contains(reflectEquals(customObject)));
Assertj хорош в этом. Особенно его пользовательская стратегия сравнения
private static class CustomClass { private final String string; CustomClass(String string) { this.string = string; } // no equals, no hashCode! } @Test public void assertjToTheRescue() { List<CustomClass> list = Arrays.asList(new CustomClass("abc")); assertThat(list).usingFieldByFieldElementComparator().contains(new CustomClass("abc")); }
Assertj предлагает множество других
usingComparator
способы.