Gymterview
junior

Как тестировать исключения в JUnit 5?

В JUnit 5 исключения тестируются через assertThrows(), который возвращает пойманное исключение для дополнительных проверок. В AssertJ используется assertThatThrownBy() с fluent API.

assertThrows (JUnit 5)

Пример
@Test
void shouldThrowWhenUserNotFound() {
    Exception ex = assertThrows(NotFoundException.class,
        () -> userService.findById(-1L));

    assertThat(ex.getMessage()).contains("not found");
}

@Test
void shouldNotThrowForValidInput() {
    assertDoesNotThrow(() -> userService.findById(1L));
}

assertThatThrownBy (AssertJ)

Пример
@Test
void shouldThrowWithDetails() {
    assertThatThrownBy(() -> userService.findById(-1L))
        .isInstanceOf(NotFoundException.class)
        .hasMessage("User not found: -1")
        .hasNoCause();
}

// Проверка вложенного исключения (cause)
@Test
void shouldWrapDatabaseException() {
    assertThatThrownBy(() -> userService.findById(999L))
        .isInstanceOf(ServiceException.class)
        .hasCauseInstanceOf(DataAccessException.class);
}

Сравнение подходов

Подход Библиотека Стиль Цепочки проверок
assertThrows() JUnit 5 Императивный Нет (отдельные assert)
assertThatThrownBy() AssertJ Fluent Да
@Test(expected=...) JUnit 4 Аннотация Нет (устарело)

Ключевые принципы

  • assertThrows возвращает исключение – можно проверить message, cause, поля
  • assertThatThrownBy (AssertJ) – fluent API с цепочкой проверок
  • В JUnit 4 использовались @Test(expected=...) и ExpectedException rule – устарело

Частые ошибки

  • @Test(expected = Exception.class) – это JUnit 4; в JUnit 5 используйте assertThrows
  • Слишком широкий тип – assertThrows(Exception.class, ...) поймает любое исключение; используйте конкретный тип
  • Не проверять message – два разных IllegalArgumentException могут означать разные ошибки

Как используется в 2026

  • assertThrows (JUnit 5) + assertThatThrownBy (AssertJ) – два стандартных подхода

На собеседовании: интервьюер может попросить написать тест на исключение. Важно показать, что вы проверяете не только тип, но и сообщение, и cause. Частая ошибка – использовать @Test(expected=...) из JUnit 4 или ловить слишком широкий тип исключения.