Как правильно проверить, что метод выбрасывает определённое исключение в JUnit?
Основные способы проверки исключений в JUnit 4
Вы столкнулись с классической задачей тестирования. Просто вызвать метод и ожидать исключение — неправильно, потому что JUnit интерпретирует любое неперехваченное исключение как ошибку теста. Вот три основных подхода, от простого к продвинутому.
1. Атрибут expected в @Test (простой способ)
Самый лаконичный вариант для JUnit 4 — указать ожидаемое исключение прямо в аннотации:
@Test(expected = IllegalArgumentException.class)
public void testDivideByZero() {
Calculator calc = new Calculator();
calc.divide(10, 0);
}
Если метод выбросит IllegalArgumentException (или его подкласс), тест пройдёт. Если исключения не будет или будет другого типа — тест упадёт. Минус: вы не можете проверить сообщение исключения или состояние объекта после его выброса.
2. Блок try-catch (гибкий, но многословный)
Позволяет проверить детали исключения:
@Test
public void testDivideByZero() {
Calculator calc = new Calculator();
try {
calc.divide(10, 0);
fail("Ожидалось IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertEquals("Делитель не может быть нулём", e.getMessage());
}
}
Ключевой момент — fail() после вызова метода. Если исключение не выбросится, выполнение дойдёт до fail() и тест провалится. Этот метод даёт полный контроль над проверкой.
3. Правило ExpectedException (JUnit 4.7+) — золотая середина
Добавьте в тестовый класс правило:
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class CalculatorTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testDivideByZero() {
Calculator calc = new Calculator();
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Делитель не может быть нулём");
calc.divide(10, 0);
}
}
Правило работает как «ожидатель»: вы настраиваете его перед вызовом метода, и если исключение совпадает — тест зелёный. Можно проверить и тип, и сообщение, и даже причину (expectCause()). Важно: вызов метода должен быть последним — если после него будет ещё код, он не выполнится.
Какой способ выбрать?
- @Test(expected = ...) — для быстрых проверок, когда важна только сигнатура исключения.
- try-catch + fail() — когда нужно проверить сообщение, cause или выполнить действия после исключения (например, убедиться, что объект остался в консистентном состоянии).
- ExpectedException — лучший выбор для 80% случаев: читаемо, гибко, не требует лишних конструкций.
В JUnit 5 (Jupiter) подход изменился — появился assertThrows(), который ещё удобнее. Но для JUnit 4 описанные методы остаются стандартом.
Альтернативный подход: JUnit 5 и assertThrows
Если вы планируете переходить на JUnit 5 (Jupiter), задача решается гораздо элегантнее. В JUnit 5 не нужно правил или атрибутов — есть статический метод assertThrows():
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
public class CalculatorTest {
@Test
public void testDivideByZero() {
Calculator calc = new Calculator();
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
calc.divide(10, 0);
});
assertEquals("Делитель не может быть нулём", exception.getMessage());
}
}
Преимущества:
- Лямбда-выражение делает код чистым и понятным.
- Метод возвращает объект исключения — можно сразу проверить его свойства.
- Не нужно использовать
@Ruleилиfail()— всё в одном вызове.
Если вы пишете новый проект, рекомендую сразу использовать JUnit 5. Он обратно совместим с JUnit 4 через механизм Vintage, но для новых тестов лучше использовать родной API.
Ответов пока нет.
Войдите чтобы оставить ответ