Есть много способов обработки исключений в JUnit ( 3 способа обработки исключений в JUnit. Какой из них выбрать?, Правило JUnit ExpectedException: помимо основ ). В этой статье я представлю библиотеку catch-exception, которую мне рекомендовали попробовать. Короче говоря, catch-exception — это библиотека, которая перехватывает исключения в одной строке кода и делает их доступными для дальнейшего анализа.
Установить через Maven
Чтобы быстро начать работу, я использовал свой демонстрационный проект Unit Testing с набором тестовых зависимостей ( JUnit, Mocito, Hamcrest, AssertJ ) и добавил исключения- ловушки :
|
1
2
3
4
5
6
|
<dependency> <groupId>com.googlecode.catch-exception</groupId> <artifactId>catch-exception</artifactId> <version>1.2.0</version> <scope>test</scope></dependency> |
Таким образом, дерево зависимостей выглядит следующим образом:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
[INFO] --- maven-dependency-plugin:2.1:tree @ unit-testing-demo ---[INFO] com.github.kolorobot:unit-testing-demo:jar:1.0.0-SNAPSHOT[INFO] +- org.slf4j:slf4j-api:jar:1.5.10:compile[INFO] +- org.slf4j:jcl-over-slf4j:jar:1.5.10:runtime[INFO] +- org.slf4j:slf4j-log4j12:jar:1.5.10:runtime[INFO] +- log4j:log4j:jar:1.2.15:runtime[INFO] +- junit:junit:jar:4.11:test[INFO] +- org.mockito:mockito-core:jar:1.9.5:test[INFO] +- org.assertj:assertj-core:jar:1.5.0:test[INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test[INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test[INFO] +- org.objenesis:objenesis:jar:1.3:test[INFO] \- com.googlecode.catch-exception:catch-exception:jar:1.2.0:test |
Начиная
Тестируемая система (SUT):
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
class ExceptionThrower { void someMethod() { throw new RuntimeException("Runtime exception occurred"); } void someOtherMethod() { throw new RuntimeException("Runtime exception occurred", new IllegalStateException("Illegal state")); } void yetAnotherMethod(int code) { throw new CustomException(code); }} |
Пример базового подхода в стиле BDD для ловли с исключениями AssertJ :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
import org.junit.Test; import static com.googlecode.catchexception.CatchException.*;import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*; public class CatchExceptionsTest { @Test public void verifiesTypeAndMessage() { when(new SomeClass()).someMethod(); then(caughtException()) .isInstanceOf(RuntimeException.class) .hasMessage("Runtime exception occurred") .hasMessageStartingWith("Runtime") .hasMessageEndingWith("occured") .hasMessageContaining("exception") .hasNoCause(); }} |
Выглядит неплохо. Кратко, читабельно. Нет бегунов JUnit. Обратите внимание, что я указал, какой метод SomeClass я ожидаю, чтобы SomeClass исключение. Как вы можете себе представить, я могу проверить несколько исключений в одном тесте. Хотя я бы не рекомендовал такой подход, так как может показаться, что он нарушает одну обязанность теста.
Кстати, если вы работаете с Eclipse, это может быть полезно для вас: улучшите поддержку содержимого для типов со статическими членами при создании тестов JUnit в Eclipse
Проверьте причину
Я думаю, что нет никакого комментария, необходимого для приведенного ниже кода:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
import org.junit.Test; import static com.googlecode.catchexception.CatchException.*;import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*; public class CatchExceptionsTest { @Test public void verifiesCauseType() { when(new ExceptionThrower()).someOtherMethod(); then(caughtException()) .isInstanceOf(RuntimeException.class) .hasMessage("Runtime exception occurred") .hasCauseExactlyInstanceOf(IllegalStateException.class) .hasRootCauseExactlyInstanceOf(IllegalStateException.class); }} |
Проверьте пользовательское исключение с Hamcrest
Для проверки пользовательского исключения я использовал код соответствия Hamcrest из моего предыдущего поста :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
class CustomException extends RuntimeException { private final int code; public CustomException(int code) { this.code = code; } public int getCode() { return code; }} class ExceptionCodeMatches extends TypeSafeMatcher<CustomException> { private int expectedCode; public ExceptionCodeMatches(int expectedCode) { this.expectedCode = expectedCode; } @Override protected boolean matchesSafely(CustomException item) { return item.getCode() == expectedCode; } @Override public void describeTo(Description description) { description.appendText("expects code ") .appendValue(expectedCode); } @Override protected void describeMismatchSafely(CustomException item, Description mismatchDescription) { mismatchDescription.appendText("was ") .appendValue(item.getCode()); }} |
И тест:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
import org.junit.Test; import static com.googlecode.catchexception.CatchException.*;import static org.junit.Assert.*; public class CatchExceptionsTest { @Test public void verifiesCustomException() { catchException(new ExceptionThrower(), CustomException.class).yetAnotherMethod(500); assertThat((CustomException) caughtException(), new ExceptionCodeMatcher(500)); }} |
Резюме
ловушка-исключение выглядит действительно хорошо. Это легко начать быстро. Я вижу некоторые преимущества по сравнению с правилом метода в JUnit. Если у меня будет возможность, я буду более тщательно исследовать библиотеку, надеюсь, в реальном проекте.
- Исходный код этой статьи можно найти здесь: Демонстрация модульного тестирования
В случае, если вы заинтересованы, пожалуйста, посмотрите на мои другие сообщения:
- 3 способа обработки исключений в JUnit. Какой выбрать?
- Правило JUnit ExpectedException: вне основ
- Как: проверить зависимости в проекте Maven (JUnit, Mocito, Hamcrest, AssertJ)
- Улучшение поддержки содержимого для типов со статическими членами при создании тестов JUnit в Eclipse
| Ссылка: | Еще один способ обработки исключений в JUnit: catch-исключение от нашего партнера по JCG Рафаля Боровца из блога Codeleak.pl . |