Ответ 1
TL;DR
-
pre-JDK8: Я рекомендую старый добрый блок
try
-catch
. -
post-JDK8: используйте AssertJ или настраиваемые lambdas для подтверждения исключительного поведения.
длинная история
Можно написать сами себе try
- catch
сделать блок или использовать инструменты JUnit (@Test(expected = ...)
или @Rule ExpectedException
JUnit rule).
Но эти способы не настолько элегантны и не смешивают хорошо читаемость с другими инструментами.
-
Блок
try
-catch
вам нужно записать блок вокруг тестируемого поведения и записать утверждение в блоке catch, это может быть хорошо, но многие находят, что этот стиль прерывает поток чтения тест. Также вам нужно написатьAssert.fail
в конце блокаtry
, иначе тест может пропустить одну сторону утверждений; PMD, findbugs или Sonar обнаружат такие проблемы. -
Функция
@Test(expected = ...)
интересна тем, что вы можете написать меньше кода, а затем записать этот тест, предположительно, менее подвержен ошибкам кодирования. Но В этом подходе отсутствуют некоторые области.- Если в тесте необходимо проверить дополнительные сведения об исключении, например причину или сообщение (хорошие сообщения об исключениях действительно важны, наличие точного типа исключения может быть недостаточно).
-
Кроме того, как ожидание помещается в методе, в зависимости от того, как написанный тестовый код написан, неправильная часть тестового кода может генерировать исключение, что приводит к ложному положительному тесту, и я не уверен, что PMD, findbugs или Sonar дадут намеки на такой код.
@Test(expected = WantedException.class) public void call2_should_throw_a_WantedException__not_call1() { // init tested tested.call1(); // may throw a WantedException // call to be actually tested tested.call2(); // the call that is supposed to raise an exception }
-
Правило
ExpectedException
также является попыткой исправить предыдущие оговорки, но это немного неудобно использовать, поскольку использует стиль ожидания, пользователи EasyMock очень хорошо знают этот стиль. Это может быть удобно для некоторых, но если вы будете следовать принципам, основанным на принципах поведения (BDD) или Arrange Act Assert (AAA), правилоExpectedException
не будет вписываться в эти стили письма. Кроме того, он может страдать от той же проблемы, что и путь@Test
, в зависимости от того, где вы помещаете ожидание.@Rule ExpectedException thrown = ExpectedException.none() @Test public void call2_should_throw_a_WantedException__not_call1() { // expectations thrown.expect(WantedException.class); thrown.expectMessage("boom"); // init tested tested.call1(); // may throw a WantedException // call to be actually tested tested.call2(); // the call that is supposed to raise an exception }
Даже ожидаемое исключение помещается перед тестом теста, оно прерывает поток чтения, если тесты следуют за BDD или AAA.
Также см. этот comment вопрос об JUnit автора
ExpectedException
.
Таким образом, эти вышеперечисленные варианты имеют всю нагрузку предостережений и явно не защищены от ошибок кодировщика.
-
Там был проект, о котором мне стало известно после создания этого ответа, который выглядит многообещающим, catch-exception.
Как говорится в описании проекта, он позволяет кодеру писать в беглой строке кода, улавливая исключение и предлагая это исключение для последующего утверждения. И вы можете использовать любую библиотеку утверждений, например Hamcrest или AssertJ.
Быстрый пример, взятый с домашней страницы:
// given: an empty list List myList = new ArrayList(); // when: we try to get the first element of the list when(myList).get(1); // then: we expect an IndexOutOfBoundsException then(caughtException()) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessage("Index: 1, Size: 0") .hasNoCause();
Как вы можете видеть, код действительно прост, вы поймаете исключение в определенной строке, API
then
- это псевдоним, который будет использовать API AssertJ (аналогично использованиюassertThat(ex).hasNoCause()...
). В какой-то момент проект опирался на FEST-Assert предка AssertJ. EDIT: Кажется, что проект запустил поддержку Java 8 Lambdas.В настоящее время эта библиотека имеет два недостатка:
-
На момент написания этой статьи стоит отметить, что эта библиотека основана на Mockito 1.x, поскольку она создает макет тестируемого объекта за сценой. Поскольку Mockito до сих пор не обновляется , эта библиотека не может работать с окончательными классами или конечными методами. И даже если бы он был основан на mockito 2 в текущей версии, для этого потребовалось бы объявить глобальный mock maker (
inline-mock-maker
), что может быть не так, как вы хотите, поскольку у этого mockmaker есть другие недостатки, которые обычный mockmaker. -
Для этого требуется еще одна тестовая зависимость.
Эти проблемы не будут применяться, если библиотека будет поддерживать lambdas, однако функциональность будет дублироваться набором инструментов AssertJ.
Принимая во внимание все, если вы не хотите использовать инструмент исключения исключений, я порекомендую старый хороший способ блока
try
-catch
, по крайней мере, до JDK7. И для пользователей JDK 8, которые вы, возможно, предпочтете использовать AssertJ, поскольку он предлагает больше, чем просто утверждение исключений. -
-
В JDK8 лямбды входят в тестовую сцену, и они оказались интересным способом утверждения исключительного поведения. AssertJ был обновлен, чтобы обеспечить хороший свободный API для подтверждения исключительного поведения.
И пример теста с AssertJ:
@Test public void test_exception_approach_1() { ... assertThatExceptionOfType(IOException.class) .isThrownBy(() -> someBadIOOperation()) .withMessage("boom!"); } @Test public void test_exception_approach_2() { ... assertThatThrownBy(() -> someBadIOOperation()) .isInstanceOf(Exception.class) .hasMessageContaining("boom"); } @Test public void test_exception_approach_3() { ... // when Throwable thrown = catchThrowable(() -> someBadIOOperation()); // then assertThat(thrown).isInstanceOf(Exception.class) .hasMessageContaining("boom"); }
-
При почти полной перезаписи JUnit 5 утверждения были немного улучшены, они могут оказаться интересными как из ящик способ утверждать правильное исключение. Но на самом деле API утверждения все еще немного беден, нет ничего вне
assertThrows
.@Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek()); Assertions.assertEquals("...", t.getMessage()); }
Как вы заметили,
assertEquals
все еще возвращаетvoid
и, как таковой, не позволяет связывать утверждения типа AssertJ.Также, если вы помните конфликт имен с
Matcher
илиAssert
, будьте готовы к тому же столкновению сAssertions
.
Я хотел бы сделать вывод, что сегодня (2017-03-03) AssertJ простота использования, API-интерфейс для обнаружения, быстрый темп разработки и как зависимость от фактического теста - лучшее решение с JDK8 независимо от тестовой среды (JUnit или нет), предыдущие JDK должны вместо этого полагаться на блоки try
- catch
, даже если они чувствуют себя неуклюжими.