java - thrown - junit expected exception message




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

Решение 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!" );
}

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

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

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

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

  assertTrue(thrown);
}

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



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"); может быть предпочтительным.


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


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

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

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

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


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

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}

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

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

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

  assertTrue(e instanceof IndexOutOfBoundsException);
}

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

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


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

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

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

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


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

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; и отлично работает с заключительными классами.


Edit Теперь, когда 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() перед foo.doStuff()

См. Эту статью для получения более подробной информации


Обновление: 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 для тестирования Exception и bad.robot - Expections Exceptions JUnit Rule .


В 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");
    }

}

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

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

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

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

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

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

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

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


В моем случае я всегда получаю 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));
}

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

    }

}






assert