Коллекция 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 5

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 , оно имеет эту функциональность из коробки:

assertThat(ImmutableList.of(frodo))
        .usingFieldByFieldElementComparator()
        .contains(frodoClone);
[9] это хитро и изворотливо, но достаточно хорошо для тестов. Пожалуйста, не используйте в производственном коде.

Я знаю два решения ваших проблем, которые используют 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 способы.