Как вы утверждаете, что в тестах JUnit 4 выбрасывается определенное исключение?

Как я могу использовать JUnit4 идиоматически, чтобы проверить, что некоторый код вызывает исключение?

Хотя я, конечно, могу сделать что-то вроде этого:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

Я вспоминаю, что есть аннотация или Assert.xyz или что- то гораздо менее хитрое и гораздо более в духе JUnit для подобных ситуаций.

1.10.2008 06:56:08
Проблема с любым другим подходом, но это то, что они неизменно заканчивают тест, как только было сгенерировано исключение. Я, с другой стороны, часто все еще хочу вызывать org.mockito.Mockito.verifyс различными параметрами, чтобы убедиться, что определенные вещи произошли (например, служба логгера была вызвана с правильными параметрами) до того, как возникло исключение.
ZeroOne 17.01.2013 11:05:54
Вы можете посмотреть, как тестировать исключения, на вики-странице JUnit github.com/junit-team/junit/wiki/Exception-testing
PhoneixS 19.02.2014 11:46:11
@ZeroOne - Для этого у меня было бы два разных теста - один для исключения и один для проверки взаимодействия с вашим макетом.
tddmonkey 25.12.2014 17:01:55
Есть способ сделать это с помощью JUnit 5, я обновил свой ответ ниже.
Dilini Rajapaksha 2.05.2017 03:05:37
30 ОТВЕТОВ
РЕШЕНИЕ

Это зависит от версии JUnit и от того, какие библиотеки утверждений вы используете.

Оригинальный ответ JUnit <= 4.12был:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {

    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);

}

Хотя ответ https://stackoverflow.com/a/31826781/2986984 имеет больше возможностей для JUnit <= 4.12.

Справка :

2338
3.03.2020 04:30:01
Этот кусок кода не будет работать, если вы ожидаете исключение только где-то в вашем коде, а не такое одеяло, как этот.
Oh Chin Boon 27.06.2011 14:50:57
@skaffman Это не сработает с org.junit.experimental.theories.Theory, управляемой org.junit.experimental.theories.Theories
Artem Oboturov 27.04.2012 16:01:48
Рой Ошеров не одобряет такого рода тестирование исключений в Искусстве модульного тестирования , поскольку исключение может находиться где угодно внутри теста, а не только внутри тестируемого блока.
Kevin Wittek 22.01.2015 14:36:08
Я не согласен с @ Kiview / Рой Ошеров. На мой взгляд, тесты должны быть для поведения, а не для реализации. Проверяя, что конкретный метод может вызвать ошибку, вы привязываете свои тесты непосредственно к реализации. Я бы сказал, что тестирование по методу, показанному выше, обеспечивает более ценный тест. Предостережение, которое я хотел бы добавить, заключается в том, что в этом случае я бы протестировал пользовательское исключение, чтобы я знал, что получаю исключение, которое действительно хочу.
nickbdyer 3.05.2016 12:22:13
Ни. Я хочу проверить поведение класса. Что важно, так это то, что если я пытаюсь получить что-то, чего нет, я получаю исключение. Тот факт, что структура данных ArrayListотвечает, не get()имеет значения. Если бы в будущем я решил перейти к примитивному массиву, мне пришлось бы изменить эту тестовую реализацию. Структура данных должна быть скрыта, чтобы тест мог сосредоточиться на поведении класса .
nickbdyer 3.05.2016 16:33:09

Изменить: Теперь, когда JUnit 5 и JUnit 4.13 были выпущены, лучший вариант будет использовать Assertions.assertThrows() (для JUnit 5) и Assert.assertThrows()(для JUnit 4.13). Смотрите мой другой ответ для деталей.

Если вы не мигрировали в JUnit 5, но можете использовать JUnit 4.7, вы можете использовать ExpectedExceptionправило:

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

Это намного лучше, чем @Test(expected=IndexOutOfBoundsException.class)потому, что тест не пройдёт, если IndexOutOfBoundsExceptionего бросить раньшеfoo.doStuff()

Смотрите эту статью для деталей

1307
20.01.2020 09:56:16
@skaffman - Если я правильно понял, похоже, что исключение.expect применяется только в рамках одного теста, а не всего класса.
bacar 6.07.2012 11:41:55
Если исключение, которое мы ожидаем получить, является проверенным исключением, следует ли добавить броски или try-catch или проверить эту ситуацию по-другому?
Mohammad Jafar Mashhadi 29.06.2013 08:05:57
@MartinTrummer Никакой код не должен запускаться после foo.doStuff (), поскольку генерируется исключение и завершается метод. Наличие кода после ожидаемого исключения (за исключением закрытия ресурсов в finally) в любом случае бесполезно, так как его никогда не следует выполнять, если выбрасывается исключение.
Jason Thompson 17.01.2014 15:59:01
Это лучший подход. Здесь есть два преимущества по сравнению с решением Скаффмана. Во-первых, у ExpectedExceptionкласса есть способы сопоставления сообщения об исключении или даже написания собственного сопоставителя, который зависит от класса исключения. Во-вторых, вы можете установить свое ожидание непосредственно перед строкой кода, в которой вы ожидаете выбросить исключение - это означает, что ваш тест не пройдёт, если неправильная строка кода выдаст исключение; в то время как нет способа сделать это с помощью решения Скаффмана.
Dawood ibn Kareem 26.07.2014 10:58:47
@MJafarMash, если проверено исключение, которое вы ожидаете выбросить, то вы бы добавили это исключение в предложение throws метода test. Вы делаете то же самое каждый раз, когда тестируете метод, который объявлен для создания проверенного исключения, даже если исключение не сработало в конкретном тестовом примере.
NamshubWriter 10.05.2015 14:38:51

Будьте осторожны, используя ожидаемое исключение, поскольку оно только утверждает, что метод вызвал это исключение, а не конкретную строку кода в тесте.

Я склонен использовать это для проверки параметров проверки, потому что такие методы, как правило, очень просты, но более сложные тесты лучше использовать:

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

Примените суждение.

471
1.10.2008 09:36:52
Может быть, я старая школа, но я все еще предпочитаю это. Это также дает мне возможность протестировать само исключение: иногда у меня возникают исключения с геттерами для определенных значений, или я могу просто искать конкретное значение в сообщении (например, искать «xyz» в сообщении «нераспознанный код« xyz »). «).
Rodney Gitzel 6.10.2010 17:22:19
Я думаю, что подход NamshubWriter дает вам лучшее из обоих миров.
Eddie 9.03.2011 19:21:57
Используя ExpectedException, вы можете вызывать N exception.expect для каждого метода, чтобы проверить, как это исключение.expect (IndexOutOfBoundsException.class); foo.doStuff1 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff2 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff3 ();
user1154664 9.10.2012 17:07:05
@ user1154664 На самом деле, вы не можете. Используя ExpectedException, вы можете только проверить, что один метод вызывает исключение, потому что, когда этот метод вызывается, тест перестает выполняться, потому что он вызвал ожидаемое исключение!
NamshubWriter 24.02.2014 16:26:40
Ваше первое предложение просто не соответствует действительности. При использовании ExpectedExceptionобычно нужно установить ожидание непосредственно перед строкой, которую вы ожидаете выбросить. Таким образом, если более ранняя строка выдает исключение, оно не сработает, и тест не пройден.
Dawood ibn Kareem 26.07.2014 10:53:36

Как уже говорилось ранее, в JUnit есть много способов справиться с исключениями. Но с Java 8 есть еще один: использование лямбда-выражений. С помощью лямбда-выражений мы можем получить такой синтаксис:

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

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

Это относительно простая, но мощная техника.

Взгляните на этот пост в блоге, описывающий эту технику: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

Исходный код можно найти здесь: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

Раскрытие: я являюсь автором блога и проекта.

212
27.11.2016 15:07:00
Мне нравится это решение, но могу ли я загрузить его из репозитория Maven?
Selwyn 31.03.2015 02:07:52
@Airduster Одна из реализаций этой идеи, доступная на Maven, - stefanbirkner.github.io/vallado
NamshubWriter 10.05.2015 14:35:20
@CristianoFontes более простая версия этого API намечена для JUnit 4.13. См github.com/junit-team/junit/commit/...
NamshubWriter 21.07.2015 02:18:27
Технически @RafalBorowiec new DummyService()::someMethodесть MethodHandle, но этот подход одинаково хорошо работает с лямбда-выражениями.
Andy 28.04.2017 19:48:45
@NamshubWriter, похоже, что Junit 4.13 был заброшен в пользу Junit 5: stackoverflow.com/questions/156503/…
Vadzim 19.12.2017 11:02:02

в junit есть четыре способа проверки исключения.

junit5.x

  • для junit5.x вы можете использовать assertThrowsследующее

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }

junit4.x

  • для junit4.x используйте необязательный атрибут «ожидается» аннотации теста

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
  • для junit4.x используйте правило ExpectedException

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
  • вы также можете использовать классический способ try / catch, широко используемый под инфраструктурой junit 3

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
  • так

    • если тебе нравится junit 5, то тебе должен понравиться первый
    • 2-й способ используется, когда вы хотите проверить только тип исключения
    • первые и последние два используются, когда вы хотите, чтобы тестовое сообщение об исключении было дальше
    • если вы используете junit 3, то 4-й является предпочтительным
  • для получения дополнительной информации, вы можете прочитать этот документ и руководство пользователя junit5 для деталей.

151
4.07.2019 14:58:01
Для меня это лучший ответ, он очень четко охватывает все пути, спасибо! Лично я продолжаю использовать третий вариант даже с Junit4 для удобства чтения, чтобы избежать пустого блока catch, вы также можете поймать Throwable и утверждать тип e
Nicolas Cornette 16.06.2016 08:57:18
Можно ли использовать ExpectedException, чтобы ожидать проверенное исключение?
miuser 2.03.2017 09:23:52
Все это - накопление трех лучших ответов. ИМО, этот ответ даже не нужно было публиковать, если он не добавляет ничего нового. Просто отвечая (популярный вопрос) для респ. Довольно бесполезно.
Paul Samsotha 5.05.2018 01:27:48
конечно, потому что вы можете передать любой тип, полученный из Trowableметода ExpectedException.expect. пожалуйста, посмотрите это подпись . @miuser
walsh 8.06.2018 13:47:20

ТЛ; др

  • post-JDK8: используйте AssertJ или пользовательские лямбды, чтобы утверждать исключительное поведение.

  • предварительно JDK8: Я буду рекомендовать хороший старый try- catchблок. ( Не забудьте добавить fail()утверждение перед catchблоком )

Независимо от Junit 4 или JUnit 5.

длинная история

Можно написать себе сделать это самостоятельно try - catchзаблокировать или использовать инструменты JUnit ( @Test(expected = ...)или @Rule ExpectedExceptionфункцию правила JUnit).

Но эти способы не так элегантно и не хорошо перемешать читаемость мудрого с другими инструментами. Кроме того, инструменты JUnit имеют некоторые подводные камни.

  1. Блок try- catchвы должны написать блок вокруг тестируемого поведения и записать утверждение в блоке catch, это может быть хорошо, но многие считают, что этот стиль прерывает поток чтения теста. Кроме того, вам нужно написать Assert.failв конце tryблока. В противном случае проверка может пропустить одну сторону утверждений; PMD , findbugs или Sonar определят такие проблемы.

  2. Эта @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
      }
  3. Это ExpectedExceptionправило также является попыткой исправить предыдущие предостережения, но его неудобно использовать, так как он использует стиль ожидания, пользователи EasyMock очень хорошо знают этот стиль. Для некоторых это может быть удобно, но если вы будете следовать принципам Behavior Driven Development (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.

    Также, смотрите этот комментарий к вопросу о JUnit автора ExpectedException. JUnit 4.13-beta-2 даже не поддерживает этот механизм:

    Запрос на извлечение № 1519 : устареть ExpectedException

    Метод Assert.assertThrows предоставляет более удобный способ проверки исключений. Кроме того, использование ExpectedException подвержено ошибкам при использовании с другими правилами, такими как TestWatcher, поскольку порядок правил важен в этом случае.

Таким образом, эти вышеупомянутые опции имеют всю их нагрузку и явно не защищены от ошибок кодера.

  1. Есть проект, о котором я узнал после создания этого ответа, который выглядит многообещающим, это ловушка-исключение .

    Как говорится в описании проекта, он позволяет кодировщику писать в беглой строке кода, перехватывающей исключение, и предлагает это исключение для последнего утверждения. И вы можете использовать любую библиотеку утверждений, такую ​​как 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();

    Как видите, код действительно прост, вы ловите исключение в определенной строке, thenAPI - это псевдоним, который будет использовать API-интерфейсы AssertJ (аналогично использованию assertThat(ex).hasNoCause()...). В какой-то момент проект опирался на FEST-Assert, предка AssertJ . РЕДАКТИРОВАТЬ: Похоже, что проект готовит поддержку Java 8 Lambdas.

    В настоящее время эта библиотека имеет два недостатка:

    • На момент написания этой статьи стоит отметить, что эта библиотека основана на Mockito 1.x, поскольку она создает макет тестируемого объекта за сценой. Поскольку Mockito все еще не обновлен, эта библиотека не может работать с финальными классами или финальными методами . И даже если бы он был основан на Mockito 2 в текущей версии, для этого потребовалось бы объявить глобальный mock maker ( inline-mock-maker), что может не соответствовать вашему желанию, так как этот mock maker имеет другие недостатки, чем обычный mock maker.

    • Требуется еще одна тестовая зависимость.

    Эти проблемы не будут применяться, если библиотека поддерживает лямбды. Однако функциональность будет дублироваться набором инструментов AssertJ.

    Принимая все во внимание, если вы не хотите использовать инструмент catch-exception, я порекомендую старый добрый способ try- catchблока, по крайней мере, до JDK7. А для пользователей JDK 8 вы можете предпочесть использовать AssertJ, поскольку он предлагает больше, чем просто утверждение исключений.

  2. С 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");
    }
  3. С почти полным переписыванием 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 или нет) ранее JDKs вместо этого следует полагаться на try-catch блоков , даже если они чувствуют себя неуклюжими.

Этот ответ был скопирован с другого вопроса, который не имеет такой же видимости, я тот же автор.

115
24.03.2020 19:27:01
Добавление org.junit.jupiter: junit-jupiter-engine: 5.0.0-RC2 (в дополнение к уже существующему junit: junit: 4.12) для возможности использования assertThrows, возможно, не является предпочтительным решением, но не вызывает каких-либо вопросы для меня.
anre 3.08.2017 12:13:39
Я фанат использования правила ExpectedException, но меня всегда беспокоило, что оно нарушает AAA. Вы написали отличную статью, чтобы описать все различные подходы, и вы определенно призвали меня попробовать AssertJ :-) Спасибо!
Pim Hazebroek 6.07.2018 06:34:44
@PimHazebroek спасибо. AssertJ API довольно богат. Лучше, на мой взгляд, то, что предлагает JUnit из коробки.
Brice 6.07.2018 07:54:41

Теперь, когда JUnit 5 и JUnit 4.13 были выпущены, лучшим вариантом будет использование Assertions.assertThrows() (для JUnit 5) и Assert.assertThrows()(для JUnit 4.13). Смотрите руководство пользователя Junit 5 .

Вот пример, который проверяет, что исключение выдается, и использует Truth, чтобы сделать утверждения в сообщении об исключении:

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

Преимущества перед подходами в других ответах:

  1. Встроенный в Юнит
  2. Вы получаете полезное сообщение об исключении, если код в лямбде не выдает исключение, и трассировку стека, если оно выдает другое исключение
  3. краткий
  4. Позволяет вашим тестам следовать Arrange-Act-Assert
  5. Вы можете точно указать, какой код вы ожидаете выбросить исключение
  6. Вам не нужно перечислять ожидаемое исключение в throwsпредложении
  7. Вы можете использовать выбранную платформу утверждений, чтобы делать утверждения о перехваченном исключении

Аналогичный метод будет добавлен org.junit Assertв JUnit 4.13.

63
9.01.2020 04:19:13
Этот подход является чистым, но я не вижу, как это позволяет нашему тесту следовать «Arrange-Act-Assert», так как мы должны заключить часть «Act» в «assertThrow», который является assert.
Clockwork 8.03.2019 15:19:24
@ Clockwork Лямбда - это «акт». Цель Arrange-Act-Assert - сделать код понятным и простым (и, следовательно, легким для понимания и сопровождения). Как вы заявили, этот подход является чистым.
NamshubWriter 11.03.2019 17:10:43
Я все еще надеялся, что смогу утвердить бросок и исключение в конце теста, хотя в части «утвердить». При таком подходе вам нужно заключить акт в первое утверждение, чтобы поймать его первым.
Clockwork 11.03.2019 18:02:00
Это потребует больше кода в каждом тесте, чтобы сделать утверждение. Это больше кода и будет подвержен ошибкам.
NamshubWriter 11.03.2019 18:36:32

Как насчет этого: поймайте очень общее исключение, убедитесь, что оно выбрано из блока catch, а затем подтвердите, что класс исключения соответствует ожидаемому. Это утверждение потерпит неудачу, если а) исключение будет неправильного типа (например, если вместо этого вы получили нулевой указатель) и б) исключение не было выброшено.

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}
42
13.11.2014 00:22:07
Кроме того, вы не увидите, что такое Exception ex в результатах теста, когда придет день, когда тест не пройден.
jontejj 14.03.2013 16:13:37
Это можно немного улучшить, изменив то, как вы утверждаете в конце. assertEquals(ExpectedException.class, e.getClass())покажет вам ожидаемые и фактические значения, когда тест не пройден.
Cypher 15.11.2018 00:54:26

Используя утверждение AssertJ , которое можно использовать вместе с JUnit:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

Это лучше, чем @Test(expected=IndexOutOfBoundsException.class)потому, что он гарантирует, что ожидаемая строка в тесте вызвала исключение, и позволяет вам проверить более подробную информацию об исключении, например, сообщение:

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

Инструкции Maven / Gradle здесь.

37
11.10.2016 18:24:43
самый краткий способ, и никто не оценит это, странно .. У меня есть только одна проблема с библиотекой assertJ, assertThat по названию конфликтует с junit. больше о assertJ throwby: JUnit: тестирование исключений с Java 8 и AssertJ 3.0.0 ~ Codeleak.pl
ycomp 21.03.2016 20:03:28
@ycomp Ну, это новый ответ на очень старый вопрос, поэтому разница в баллах обманчива.
weston 24.03.2016 08:46:58
Это, вероятно, лучшее решение, если можно использовать Java 8 и AssertJ!
Pierre Henry 25.03.2016 15:01:59
@ycomp Я подозреваю, что этот конфликт имен может быть умышленным: поэтому библиотека AssertJ настоятельно рекомендует вам никогда не использовать JUnit assertThat, всегда AssertJ. Кроме того, метод JUnit возвращает только «обычный» тип, тогда как метод AssertJ возвращает AbstractAssertподкласс ..., позволяющий структурировать методы, как указано выше (или любые другие технические термины для этого ...).
mike rodent 11.10.2016 16:13:43
@ Weston на самом деле я только что использовал вашу технику в AssertJ 2.0.0. Без сомнения, нет оправдания для обновления, но, возможно, вы хотели бы знать.
mike rodent 11.10.2016 16:14:47

Решение в стиле BDD : JUnit 4 + Catch Exception + AssertJ

import static com.googlecode.catchexception.apis.BDDCatchException.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(() -> foo.doStuff());

    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

}

зависимости

eu.codearte.catch-exception:catch-exception:2.0
37
25.09.2019 10:10:00

Чтобы решить ту же проблему, я создал небольшой проект: http://code.google.com/p/catch-exception/

С помощью этого маленького помощника вы бы написали

verifyException(foo, IndexOutOfBoundsException.class).doStuff();

Это менее многословно, чем правило ExpectedException в JUnit 4.7. По сравнению с решением, предоставляемым skaffman, вы можете указать, в какой строке кода вы ожидаете исключения. Надеюсь, это поможет.

33
28.10.2011 09:26:21
Я подумал о том, чтобы сделать что-то подобное, но в конечном итоге обнаружил, что истинная сила ExpectedException заключается в том, что вы можете не только указать ожидаемое исключение, но вы также можете указать некоторые свойства исключения, такие как ожидаемая причина или ожидаемое сообщение.
Jason Thompson 17.01.2014 16:28:09
Я думаю, что это решение имеет те же недостатки, что и макеты? Например, если fooэто finalбудет не потому , что вы не можете прокси foo?
Tom 20.06.2014 19:39:17
Том, если doStuff () является частью интерфейса, прокси-подход будет работать. В противном случае этот подход потерпит неудачу, вы правы.
rwitzel 16.01.2015 09:49:32

Обновление: JUnit5 имеет усовершенствование для тестирования исключений: assertThrows.

Следующий пример из: Руководство пользователя Junit 5

 @Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> 
    {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

Оригинальный ответ с использованием JUnit 4.

Есть несколько способов проверить, что выброшено исключение. Я также обсудил ниже варианты в моем посте Как написать отличные модульные тесты с JUnit

Установите expectedпараметр @Test(expected = FileNotFoundException.class).

@Test(expected = FileNotFoundException.class) 
public void testReadFile() { 
    myClass.readFile("test.txt");
}

С помощью try catch

public void testReadFile() { 
    try {
        myClass.readFile("test.txt");
        fail("Expected a FileNotFoundException to be thrown");
    } catch (FileNotFoundException e) {
        assertThat(e.getMessage(), is("The file test.txt does not exist!"));
    }

}

Тестирование с ExpectedExceptionправилом.

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void testReadFile() throws FileNotFoundException {

    thrown.expect(FileNotFoundException.class);
    thrown.expectMessage(startsWith("The file test.txt"));
    myClass.readFile("test.txt");
}

Вы можете прочитать больше о тестировании исключений в вики JUnit4 для тестирования исключений и bad.robot - Правило исключения JUnit для исключений .

31
5.01.2018 16:03:12

Вы также можете сделать это:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}
22
13.11.2014 00:21:01
В тестах JUnit лучше использовать Assert.fail(), а не assertна тот случай, если ваши тесты выполняются в среде, где утверждения не включены.
NamshubWriter 14.01.2015 04:51:01

ИМХО, лучший способ проверить наличие исключений в JUnit - это шаблон try / catch / fail / assert:

// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
    sut.doThing();
    fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
                         // otherwise you may catch an exception for a dependency unexpectedly
    // a strong assertion on the message, 
    // in case the exception comes from anywhere an unexpected line of code,
    // especially important if your checking IllegalArgumentExceptions
    assertEquals("the message I get", e.getMessage()); 
}

Это assertTrueможет быть немного сильным для некоторых людей, поэтому assertThat(e.getMessage(), containsString("the message");может быть предпочтительнее.

14
14.03.2017 18:07:21

Решение JUnit 5

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}

Больше информации о JUnit 5 на http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions

13
24.07.2016 15:00:31

Я попробовал многие методы здесь, но они были либо сложными, либо не совсем отвечали моим требованиям. На самом деле, можно написать вспомогательный метод довольно просто:

public class ExceptionAssertions {
    public static void assertException(BlastContainer blastContainer ) {
        boolean caughtException = false;
        try {
            blastContainer.test();
        } catch( Exception e ) {
            caughtException = true;
        }
        if( !caughtException ) {
            throw new AssertionFailedError("exception expected to be thrown, but was not");
        }
    }
    public static interface BlastContainer {
        public void test() throws Exception;
    }
}

Используйте это так:

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});

Нулевые зависимости: нет необходимости в mockito, нет необходимости в powermock; и отлично работает с выпускными классами.

12
10.10.2012 15:02:44
Интересно, но не подходит для AAA (Arrange Act Assert), где вы хотите выполнить действия Act и Assert фактически разными шагами.
bln-tom 28.10.2014 21:15:48
@ bln-tom Технически это два разных шага, они просто не в таком порядке. ; p
Trejkaz 2.02.2015 03:52:35

Самый гибкий и элегантный ответ для Junit 4 я нашел в блоге Mkyong . Он имеет гибкость try/catchиспользования @Ruleаннотации. Мне нравится этот подход, потому что вы можете прочитать определенные атрибуты настроенного исключения.

package com.mkyong;

import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;

public class Exception3Test {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void testNameNotFoundException() throws NameNotFoundException {

        //test specific type of exception
        thrown.expect(NameNotFoundException.class);

        //test message
        thrown.expectMessage(is("Name is empty!"));

        //test detail
        thrown.expect(hasProperty("errCode"));  //make sure getters n setters are defined.
        thrown.expect(hasProperty("errCode", is(666)));

        CustomerService cust = new CustomerService();
        cust.findByName("");

    }

}
12
8.11.2018 13:28:31

Решение Java 8

Если вы хотите решение, которое:

  • Использует лямбды Java 8
  • Разве не зависит ни от какой JUnit магии
  • Позволяет проверить наличие нескольких исключений в одном методе тестирования.
  • Проверяет исключение, генерируемое определенным набором строк в вашем методе тестирования вместо любой неизвестной строки во всем методе теста
  • Возвращает действительный объект исключения, который был сгенерирован, чтобы вы могли его дополнительно изучить

Вот полезная функция, которую я написал:

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

( взято из моего блога )

Используйте это следующим образом:

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}
10
11.02.2018 16:38:50

JUnit имеет встроенную поддержку для этого с атрибутом «ожидаемый»

9
1.10.2008 07:05:49

В моем случае я всегда получаю RuntimeException от db, но сообщения различаются. И исключение должно быть обработано соответственно. Вот как я это проверил:

@Test
public void testThrowsExceptionWhenWrongSku() {

    // Given
    String articleSimpleSku = "999-999";
    int amountOfTransactions = 1;
    Exception exception = null;

    // When
    try {
        createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
    } catch (RuntimeException e) {
        exception = e;
    }

    // Then
    shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}

private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
    assertNotNull(e);
    assertTrue(e.getMessage().contains(message));
}
8
2.07.2013 09:03:57
перед строкой } catch (, вы должны вставитьfail("no exception thrown");
Daniel Alder 18.12.2017 09:40:15

Просто сделайте Matcher, который можно выключать и включать, вот так:

public class ExceptionMatcher extends BaseMatcher<Throwable> {
    private boolean active = true;
    private Class<? extends Throwable> throwable;

    public ExceptionMatcher(Class<? extends Throwable> throwable) {
        this.throwable = throwable;
    }

    public void on() {
        this.active = true;
    }

    public void off() {
        this.active = false;
    }

    @Override
    public boolean matches(Object object) {
        return active && throwable.isAssignableFrom(object.getClass());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("not the covered exception type");
    }
}

Чтобы использовать это:

public ExpectedException exception = ExpectedException.none();затем добавьте :

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
6
2.03.2016 02:28:41

В JUnit 4 или более поздней версии вы можете проверить исключения следующим образом

@Rule
public ExpectedException exceptions = ExpectedException.none();


это предоставляет множество функций, которые можно использовать для улучшения наших тестов JUnit.
Если вы видите пример ниже, я тестирую 3 вещи на исключение.

  1. Тип создаваемого исключения
  2. Сообщение об исключении
  3. Причина исключения


public class MyTest {

    @Rule
    public ExpectedException exceptions = ExpectedException.none();

    ClassUnderTest classUnderTest;

    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
    }

    @Test
    public void testAppleisSweetAndRed() throws Exception {

        exceptions.expect(Exception.class);
        exceptions.expectMessage("this is the exception message");
        exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));

        classUnderTest.methodUnderTest("param1", "param2");
    }

}
6
8.12.2016 05:48:19

Мы можем использовать утверждение сбоя после метода, который должен вернуть исключение:

try{
   methodThatThrowMyException();
   Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
   // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
   assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
   // In case of verifying the error message
   MyException myException = (MyException) exception;
   assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}
6
20.12.2017 10:34:05
Второй catchпроглотит трассировку стека, если будет
NamshubWriter 30.08.2015 01:06:51

В дополнение к тому, что сказал NamShubWriter , убедитесь, что:

  • Экземпляр ExpectedException является открытым ( связанный вопрос )
  • ExpectedException не создается, например, в методе @Before. Этот пост ясно объясняет все тонкости порядка исполнения JUnit.

Вы не делать этого:

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}

Наконец, этот блог ясно иллюстрирует, как утверждать, что выбрасывается определенное исключение.

5
23.05.2017 12:18:24

Я рекомендую библиотеку assertj-coreдля обработки исключений в тесте junit

В Java 8, вот так:

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);
4
18.07.2018 11:32:37

Решение Junit4 с Java8 заключается в использовании этой функции:

public Throwable assertThrows(Class<? extends Throwable> expectedException, java.util.concurrent.Callable<?> funky) {
    try {
        funky.call();
    } catch (Throwable e) {
        if (expectedException.isInstance(e)) {
            return e;
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
    }
    throw new AssertionError(
            String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
}

Использование тогда:

    assertThrows(ValidationException.class,
            () -> finalObject.checkSomething(null));

Обратите внимание, что единственным ограничением является использование finalссылки на объект в лямбда-выражении. Это решение позволяет продолжить тестовые утверждения вместо ожидаемых результатов на уровне метода с использованием @Test(expected = IndexOutOfBoundsException.class)решения.

2
6.04.2018 16:32:25

Взять, к примеру, вы хотите написать Junit для указанного ниже фрагмента кода.

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

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

 @Rule
public ExpectedException exception=ExpectedException.none();

private Demo demo;
@Before
public void setup(){

    demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {

    demo.divideByZeroDemo(5, 0);

}

@Test
public void testExceptionWithMessage(){


    exception.expectMessage("Array is out of bound");
    exception.expect(ArrayIndexOutOfBoundsException.class);
    demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}
1
29.10.2016 07:34:18
    @Test(expectedException=IndexOutOfBoundsException.class) 
    public void  testFooThrowsIndexOutOfBoundsException() throws Exception {
         doThrow(IndexOutOfBoundsException.class).when(foo).doStuff();  
         try {
             foo.doStuff(); 
            } catch (IndexOutOfBoundsException e) {
                       assertEquals(IndexOutOfBoundsException .class, ex.getCause().getClass());
                      throw e;

               }

    }

Вот еще один способ проверить, правильно ли выдается метод исключения или нет.

1
16.04.2019 12:54:07

В Java 8 вы можете создать метод, принимающий код для проверки и ожидаемое исключение в качестве параметров:

private void expectException(Runnable r, Class<?> clazz) { 
    try {
      r.run();
      fail("Expected: " + clazz.getSimpleName() + " but not thrown");
    } catch (Exception e) {
      if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
    }
  }

а затем внутри вашего теста:

expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);

Выгоды:

  • не полагаясь ни на какую библиотеку
  • локализованная проверка - более точная и позволяет при необходимости иметь несколько утверждений в одном тесте
  • легко использовать
0
1.10.2017 12:03:38

Мое решение с использованием Java 8 лямбды:

public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
    try {
        action.run();
        Assert.fail("Did not throw expected " + expected.getSimpleName());
        return null; // never actually
    } catch (Throwable actual) {
        if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
            System.err.println("Threw " + actual.getClass().getSimpleName() 
                               + ", which is not a subtype of expected " 
                               + expected.getSimpleName());
            throw actual; // throw the unexpected Throwable for maximum transparency
        } else {
            return (T) actual; // return the expected Throwable for further examination
        }
    }
}

Вы должны определить FunctionalInterface, потому Runnableчто не объявляет требуемый throws.

@FunctionalInterface
public interface ThrowingRunnable {
    void run() throws Throwable;
}

Метод может быть использован следующим образом:

class CustomException extends Exception {
    public final String message;
    public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
    throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);
0
4.10.2017 12:03:26