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

1753

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

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

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

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

  assertTrue(thrown);
}

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

  • 20
    Проблема с любым другим подходом, но это то, что они неизменно заканчивают тест, как только было сгенерировано исключение. Я, с другой стороны, часто все еще хочу вызвать org.mockito.Mockito.verify с различными параметрами, чтобы удостовериться, что определенные вещи произошли (например, служба org.mockito.Mockito.verify журнала была вызвана с правильными параметрами) до того, как возникло исключение.
  • 5
    Вы можете увидеть, как тестировать исключения в вики-странице JUnit github.com/junit-team/junit/wiki/Exception-testing
Показать ещё 2 комментария
Теги:
junit
exception
junit4
assert

33 ответа

2004
Лучший ответ

JUnit 4 поддерживает это:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

Ссылка: https://junit.org/junit4/faq.html#atests_7

  • 59
    Этот фрагмент кода не будет работать, если вы ожидаете исключение только где-то в вашем коде, а не такое одеяло, как этот.
  • 4
    @skaffman Это не сработает с org.junit.experimental.theories.Theory, управляемой org.junit.experimental.theories.Theories.
Показать ещё 14 комментариев
1279

Изменить Теперь, когда JUnit5 выпустил, лучшим вариантом будет использование Assertions.assertThrows() (см. мой другой ответ).

Если вы не перешли на 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()

Подробнее см. в этой статье

  • 13
    @skaffman - Если я правильно понял, похоже, что исключение.expect применяется только в одном тесте, а не во всем классе.
  • 4
    Если исключение, которое мы ожидаем получить, является проверенным исключением, следует ли добавить броски или try-catch или протестировать эту ситуацию по-другому?
Показать ещё 19 комментариев
429

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

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

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

Применить решение.

  • 88
    Может быть, я старая школа, но я все еще предпочитаю это. Это также дает мне возможность протестировать само исключение: иногда у меня возникают исключения с геттерами для определенных значений, или я могу просто искать конкретное значение в сообщении (например, искать «xyz» в сообщении «нераспознанный код« xyz »). «).
  • 3
    Я думаю, что подход NamshubWriter дает вам лучшее из обоих миров.
Показать ещё 8 комментариев
206

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

@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

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

  • 2
    Мне нравится это решение, но могу ли я загрузить его из репозитория Maven?
  • 0
    @Airduster Одна из реализаций этой идеи, доступная на Maven, - stefanbirkner.github.io/vallado.
Показать ещё 5 комментариев
95

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

  • для 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.
        }
    }
    
  • наконец, для junit5.x, вы также можете использовать assertThrows следующим образом:

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

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

  • 6
    Для меня это лучший ответ, он охватывает все пути очень четко, спасибо! Лично я продолжаю использовать 3-й вариант даже с Junit4 для удобства чтения, чтобы избежать пустого блока catch, вы также можете перехватывать Throwable и утверждать тип e
  • 0
    Можно ли использовать ExpectedException, чтобы ожидать проверенное исключение?
Показать ещё 2 комментария
70

ТЛ; др

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

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

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

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

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

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

  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: отменить ожидаемое исключение

    Метод 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();
    

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

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

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

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

    Эти проблемы не будут применяться, если библиотека будет поддерживать лямбды, однако функциональность будет дублироваться набором инструментов 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 или нет) предыдущие JDK вместо этого должны полагаться на блоки try - catch даже если они чувствуют себя неуклюже.

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

  • 1
    Добавление org.junit.jupiter: junit-jupiter-engine: 5.0.0-RC2 (в дополнение к уже существующему junit: junit: 4.12) для возможности использования assertThrows, возможно, не является предпочтительным решением, но не вызывает каких-либо вопросы для меня.
  • 0
    Я фанат использования правила ExpectedException, но меня всегда беспокоило, что оно нарушает правила AAA. Вы написали отличную статью, чтобы описать все различные подходы, и вы определенно побудили меня попробовать AssertJ :-) Спасибо!
Показать ещё 1 комментарий
38

Теперь, когда JUnit 5 выпустил, лучший вариант - использовать Assertions.assertThrows() (см. Руководство пользователя 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!");
  }
}

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

  • Встроенный JUnit
  • Вы получаете полезное сообщение об исключении, если код в лямбда не генерирует исключение, а stacktrace, если он выдает другое исключение.
  • Сжатый
  • Позволяет вашим тестам следовать Arrange-Act-Assert
  • Вы можете точно указать, какой код вы собираетесь генерировать исключение.
  • Вам не нужно указывать ожидаемое исключение в предложении throws
  • Вы можете использовать структуру утверждений по своему выбору, чтобы делать утверждения об исключении catch

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

  • 0
    Этот подход является чистым, но я не вижу, как это позволяет нашему тесту следовать «Arrange-Act-Assert», так как мы должны заключить часть «Act» в «assertThrow», который является assert.
  • 0
    @Clockwork Лямбда - это «акт». Цель Arrange-Act-Assert - сделать код понятным и простым (и, следовательно, легким для понимания и сопровождения). Как вы заявили, этот подход чистый.
Показать ещё 2 комментария
36

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

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

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

  assertTrue(e instanceof IndexOutOfBoundsException);
}
  • 4
    Это то, что вы делаете в JUnit 3. Junit 4 делает это лучше.
  • 3
    Кроме того, вы не увидите, что такое Exception ex в результатах теста, когда придет день, когда тест не пройден.
Показать ещё 1 комментарий
32

Используя 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 здесь.

  • 0
    самый краткий способ, и никто не оценит это, странно .. У меня есть только одна проблема с библиотекой assertJ, assertThat по названию конфликтует с junit. больше о assertJ throwby: JUnit: тестирование исключений с Java 8 и AssertJ 3.0.0 ~ Codeleak.pl
  • 0
    @ycomp Ну, это новый ответ на очень старый вопрос, поэтому разница в баллах обманчива.
Показать ещё 5 комментариев
31

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

Используя этот маленький помощник, вы должны написать

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

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

  • 0
    Я подумал о том, чтобы сделать что-то подобное, но в конечном итоге обнаружил, что истинная сила ExpectedException заключается в том, что вы можете не только указать ожидаемое исключение, но вы также можете указать некоторые свойства исключения, такие как ожидаемая причина или ожидаемое сообщение.
  • 0
    Вы также можете сделать это с catchException
Показать ещё 2 комментария
29

BDD Стиль Решение: JUnit 4 + Catch Exception + AssertJ

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(foo).doStuff();

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

}

Исходный код

Зависимости

eu.codearte.catch-exception:catch-exception:1.3.3
24

Обновление: 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 wiki для тестирования исключений и bad.robot - Ожидание исключений Правило JUnit.

21

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

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

IMHO, лучший способ проверить исключения в 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"); может быть предпочтительнее.

11

Решение 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

10

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

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, нет необходимости в powerermock; и отлично работает с окончательными классами.

  • 0
    Интересно, но не подходит для AAA (Arrange Act Assert), где вы хотите выполнить действия Act и Assert фактически разными шагами.
  • 1
    @ bln-tom Технически это два разных шага, они просто не в таком порядке. ;п
8

Самый гибкий и элегантный ответ для 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("");

    }

}
8

Решение Java 8

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

  • Использует Java 8 lambdas
  • Не зависит от какой-либо магии 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!" );
}
8

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

7

В моем случае я всегда получаю 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));
}
  • 1
    перед строкой с } catch ( , вы должны вставить fail("no exception thrown");
5

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

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

Это дает множество функций, которые могут быть использованы для улучшения наших тестов JUnit.
Если вы видите приведенный ниже пример, я тестирую 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");
    }

}
5

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

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());
}
  • 3
    Второй catch проглотит трассировку стека, если выдается какое-то другое исключение, теряя полезную информацию
5

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

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();
4

Кроме того, что сказал NamShubWriter, убедитесь, что:

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

Сделайте не:

@Rule    
public ExpectedException expectedException;

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

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

1

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

В java 8, вот так:

//given

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

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);
1

Возьмем, к примеру, вы хотите написать 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"});
}
0

Решение 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 объект в лямбда-выражении. Это решение позволяет продолжить утверждение теста вместо того, чтобы ожидать thowable на уровне метода, используя @Test(expected = IndexOutOfBoundsException.class).

0

Односторонний обменный шаблон больше не поддерживается версией HTTP Endpoint.latest Anypoint Studio.

https://1.gravatar.com/avatar/42d13a27f6019502ab66b6aa820b10ea?s=50&d=identicon&r=G 'class=' avatar avatar-50 'height = '50' width = '50 '/" > http://synq.cloud/'rel=' external nofollow 'class=' url ' > Мэтьюз говорит:

Изменено выражение # [message.inboundProperties.http.request.uri!= '/favicon.ico] в 5.1.0, чтобы заставить его работать выражение = "# [message.inboundProperties.http.request!=/favicon.ico]" с версией Anypoint Studio 5.2.0 выражение = "# [message.inboundProperties ['http.request.path]! ='/favicon.ico]"

Надеюсь, что это поможет!

Приветствия,

0

Существует два способа написания тестового примера

  • Аннотировать тест с исключением, которое выбрано методом. Что-то вроде этого @Test(expected = IndexOutOfBoundsException.class)
  • Вы можете просто поймать исключение в тестовом классе, используя блок catch try и утвердить сообщение, которое выбрано из метода в тестовом классе.

    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
    

Я надеюсь, что это ответит на ваш запрос Счастливое обучение...

0

Мое решение с использованием Java 8 lambdas:

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

С помощью 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);

Преимущества:

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

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

Я использовал assertTrue (boolean) в сочетании с try/catch для поиска моего ожидаемого исключения. Вот пример:

public void testConstructor() {
    boolean expectedExceptionThrown;
    try {
        // Call constructor with bad arguments
        double a = 1;
        double b = 2;
        double c = a + b; // In my example, this is an invalid option for c
        new Triangle(a, b, c);
        expectedExceptionThrown = false; // because it successfully constructed the object
    }
    catch(IllegalArgumentException e) {
        expectedExceptionThrown = true; // because I'm in this catch block
    }
    catch(Exception e) {
        expectedExceptionThrown = false; // because it threw an exception but not the one expected
    }
    assertTrue(expectedExceptionThrown);
}
  • 2
    Второй catch проглотит трассировку стека, если выдается какое-то другое исключение, теряя полезную информацию
-4
try {
    my method();
    fail( "This method must thrwo" );
} catch (Exception ex) {
    assertThat(ex.getMessage()).isEqual(myErrormsg);
}
  • 4
    Вы отвечаете на вопрос 10-летней давности, пожалуйста, расширьте свой ответ, почему вы считаете, что он дает что-то новое, что принятый ответ не распространяется. Также всегда старайтесь рассказать что-нибудь о своем коде и предоставить документацию или объяснение.
  • 1
    По сути, это дубликат этого 10-летнего ответа (за исключением проверки сообщения об ошибке).
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню