[Перевод] Soft Assertions в AssertJ

1. Введение

В этой статье мы познакомимся с Soft Assertions в AssertJ, подробно рассмотрим, зачем они нужны и обсудим похожие решения в других фреймворках для тестирования. 

2. Мотивация

Для начала поймем, зачем нужны Soft Assertions. Возьмем следующий пример:

@Test
void test_HardAssertions() {
    RequestMapper requestMapper = new RequestMapper();

    DomainModel result = requestMapper.map(new Request().setType("COMMON"));

    Assertions.assertThat(result.getId()).isNull();

    Assertions.assertThat(result.getType()).isEqualTo(1);

    Assertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
}

Этот код проверяет, правильно ли маппер преобразует объект Request в DomainModel. Мы ожидаем, что все assertion`ы пройдут, если маппер работает верно.

Теперь давайте представим, что маппер содержит дефект и отображает id и status неправильно. В таком случае запуск теста приведет к AssertionFailedError:

org.opentest4j.AssertionFailedError: 

expected: null

but was: "73a3f292-8131-4aa9-8d55-f0dba77adfdb"

Отлично, что мы можем увидеть, почему тест не прошел. Но есть нюанс: мы знаем, что поле id было помаплено некорректно, а вот про то, что и поле status было помаплено не так как мы ожидали, мы узнаем только при повторном запуске теста. Причина этому — Hard Assertions, которые мы используем в AssertJ. Иными словами, первая неудачная проверка вызывает AssertionError немедленно, и последующие проверки не выполняются.

На первый взгляд кажется, что это небольшая проблема: мы запустили бы тест дважды — сначала, чтобы обнаружить некорректное отображение id, а затем, чтобы увидеть аналогичную ошибку с полем status. Однако, когда наш маппер более сложный и работает с десятками полей (как обычно и бывает в реальных проектах), постоянный перезапуск тестов будет отнимать много времени. Чтобы решить эту проблему, AssertJ предлагает Soft Assertions.

3. Soft Assertions в AssertJ

Soft assertions решают эту проблему, собирая все ошибки в единый отчет. Такой подход упрощает отладку и экономит время. Давайте посмотрим, как soft assertions работают в AssertJ, переписав наш тест:

@Test
void test_softAssertions() {
    RequestMapper requestMapper = new RequestMapper();

    DomainModel result = requestMapper.map(new Request().setType("COMMON"));

    SoftAssertions.assertSoftly(softAssertions -> {

        softAssertions.assertThat(result.getId()).isNull()
        softAssertions.assertThat(result.getType()).isEqualTo(1);
        softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");
    });
}

AssertJ позволяет выполнить набор assertion«ов мягко: все проверки в указанном лямбда-выражении будут выполнены, даже если некоторые из них вызовут ошибки. После выполнения теста все ошибки будут собраны в один общий отчет, как показано ниже:

org.assertj.core.error.AssertJMultipleFailuresError: 

Multiple Failures (3 failures)

-- failure 1 --

expected: null

 but was: "66f8625c-b5e4-4705-9a49-94db3b347f72"

at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:19)

-- failure 2 --

expected: 1

 but was: 0

at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:20)

-- failure 3 --

expected: "DRAFT"

 but was: "NEW"

at SoftAssertionsUnitTest.lambda$test_softAssertions$0(SoftAssertionsUnitTest.java:21)

Этa возможность AssertJ очень полезна при отладке, так как сокращает время на поиск багов. Также стоит упомянуть, что существует другой способ написания тестов с soft assertions в AssertJ.

@Test

void test_softAssertionsViaInstanceCreation() {
    RequestMapper requestMapper = new RequestMapper();

    DomainModel result = requestMapper.map(new Request().setType("COMMON"));

    SoftAssertions softAssertions = new SoftAssertions();

    softAssertions.assertThat(result.getId()).isNull();

    softAssertions.assertThat(result.getType()).isEqualTo(1);

    softAssertions.assertThat(result.getStatus()).isEqualTo("DRAFT");

    softAssertions.assertAll();
}

В этом случае мы создаем экземпляр класса SoftAssertions напрямую, в отличие от предыдущего примера, где экземпляр создается скрыто, с помощью фреймворка. 

Оба подхода идентичны по функциональности, поэтому можно использовать любой из них по своему усмотрению.

4. Soft Assertions в других тестовых фреймворках

Поскольку soft assertions очень полезны, многие фреймворки для тестирования уже внедрили эту функциональность. Однако в разных фреймворках она может называться по-разному или не иметь названия вовсе. Например, в Junit5 есть метод assertAll (), который работает схожим образом, но имеет свои особенности. В TestNG тоже есть Soft Assertions, реализация которых очень похожа на реализацию в AssertJ. В целом, большинство популярных фреймворков для тестирования поддерживают soft assertions, даже если они называются иначе.

5. Подведем итоги

Давайте подытожим различия и сходства между hard и soft assertions в одной таблице:

Hard Assertions

Soft Assertions

Является режимом работы assertion по умолчанию

да

нет

Поддерживается большинством фреймворков (включая AssertJ)

да

да

Демонстрирует безотказное поведение

да

нет

6. Заключение

В этой статье мы рассмотрели soft assertions и их преимущества. В отличие от hard assertions, которые прерывают тест при ошибке, soft assertions позволяют продолжить выполнение теста, даже если некоторые assert«ы не прошли, после чего мы получим подробный отчет об ошибках. Такой подход значительно облегчает и ускоряет отладку.

Стоит отметить, что soft assertions не являются уникальной фичей AssertJ — аналогичные возможности есть и в других популярных фреймворках, хотя они могут называться иначе или вовсе не иметь специального названия.

Как всегда, код из статьи доступен на GitHub.

2b78a26eb970b2f2dd5b142d7f141160.png

Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.

Ждем всех,  присоединяйтесь!  

© Habrahabr.ru