В Java, как я могу проверить выброшенное исключение с помощью JUnit? - PullRequest
28 голосов
/ 24 апреля 2009

При написании модульных тестов для Java API могут быть обстоятельства, когда вы хотите выполнить более детальную проверку исключения. То есть больше, чем предлагает аннотация @ test , предлагаемая JUnit.

Например, рассмотрим класс, который должен перехватывать исключение из какого-либо другого интерфейса, обернуть это исключение и выбросить обернутое исключение. Вы можете проверить:

  1. Точный вызов метода, который выбрасывает исключенную оболочку.
  2. То, что исключение оболочки имеет исходное исключение в качестве причины.
  3. Сообщение об исключении оболочки.

Суть в том, что вы хотите провести дополнительную проверку исключения в модульном тесте (не спор о том, должен ли проверять такие вещи, как сообщение об исключении).

Какой хороший подход для этого?

Ответы [ 10 ]

24 голосов
/ 24 апреля 2009

Как указано в ваш ответ , это хороший подход. В дополнение к этому:

Вы можете заключить функцию expectException в новую аннотацию под названием ExpectedException.
Аннотированный метод будет выглядеть так:

@Test
@ExpectedException(class=WrapperException.class, message="Exception Message", causeException)
public void testAnExceptionWrappingFunction() {
  //whatever you test
}

Этот способ был бы более читабельным, но это точно такой же подход.

Другая причина: мне нравятся аннотации:)

21 голосов
/ 24 октября 2012

В JUnit 4 это легко сделать, используя ExpectedException rule.

Вот пример из javadocs:

// These tests all pass.
public static class HasExpectedException {
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void throwsNothing() {
        // no exception expected, none thrown: passes.
    }

    @Test
    public void throwsNullPointerException() {
        thrown.expect(NullPointerException.class);
        throw new NullPointerException();
    }

    @Test
    public void throwsNullPointerExceptionWithMessage() {
        thrown.expect(NullPointerException.class);
        thrown.expectMessage("happened?");
        thrown.expectMessage(startsWith("What"));
        throw new NullPointerException("What happened?");
    }
}
18 голосов
/ 25 апреля 2009

Глядя на предлагаемые ответы, вы действительно чувствуете боль от отсутствия замыканий в Java. ИМХО, самое читаемое решение - это старая добрая попытка поймать.

@Test
public void test() {
    ...
    ...
    try {
        ...
        fail("No exception caught :(");
    }
    catch (RuntimeException ex) {
        assertEquals(Whatever.class, ex.getCause().getClass());
        assertEquals("Message", ex.getMessage());
    }
}
11 голосов
/ 24 апреля 2009

Для JUNIT 3.x

public void test(){
   boolean thrown = false;
   try{
      mightThrowEx();
   } catch ( Surprise expected ){
      thrown = true;
      assertEquals( "message", expected.getMessage());
   }
  assertTrue(thrown );
}
5 голосов
/ 24 апреля 2009

До этого поста я сделал проверку исключений, выполнив это:

try {
    myObject.doThings();
    fail("Should've thrown SomeException!");
} catch (SomeException e) {
    assertEquals("something", e.getSomething());
}

Я потратил несколько минут на обдумывание проблемы и придумал следующее (Java5, JUnit 3.x):

// Functor interface for exception assertion.
public interface AssertionContainer<T extends Throwable> {
    void invoke() throws T;
    void validate(T throwable);
    Class<T> getType();
}

// Actual assertion method.
public <T extends Throwable> void assertThrowsException(AssertionContainer<T> functor) {
    try {
        functor.invoke();
        fail("Should've thrown "+functor.getType()+"!");
    } catch (Throwable exc) {
        assertSame("Thrown exception was of the wrong type! Expected "+functor.getClass()+", actual "+exc.getType(),
                   exc.getClass(), functor.getType());
        functor.validate((T) exc);
    }
}

// Example implementation for servlet I used to actually test this. It was an inner class, actually.
AssertionContainer<ServletException> functor = new AssertionContainer<ServletException>() {
    public void invoke() throws ServletException {
        servlet.getRequiredParameter(request, "some_param");
    }

    public void validate(ServletException e) {
        assertEquals("Parameter \"some_param\" wasn't found!", e.getMessage());
    }

    public Class<ServletException> getType() {
        return ServletException.class;
    }
}

// And this is how it's used.
assertThrowsException(functor);

Глядя на этих двоих, я не могу решить, какой из них мне больше нравится. Я предполагаю, что это одна из тех проблем, когда достижение цели (в моем случае, метода утверждения с параметром functor) не стоит этого в долгосрочной перспективе, поскольку сделать эти 6+ кода для утверждения попытки намного проще ..catch block.

Опять же, может быть, мой 10-минутный результат решения проблем в пятницу вечером - не самый разумный способ сделать это.

4 голосов
/ 22 июня 2012

@ akuhn:

Даже без замыканий мы можем получить более удобочитаемое решение (используя catch-exception ):

import static com.googlecode.catchexception.CatchException.*;

public void test() {
    ...
    ...
    catchException(nastyBoy).doNastyStuff();
    assertTrue(caughtException() instanceof WhateverException);
    assertEquals("Message", caughtException().getMessage());
}
2 голосов
/ 24 апреля 2009

я сделал что-то очень простое

testBla(){
    try {
        someFailingMethod()
        fail(); //method provided by junit
    } catch(Exception e) {
          //do nothing
    }
}
2 голосов
/ 24 апреля 2009

Следующий вспомогательный метод (адаптированный из этого сообщения в блоге) делает свое дело:

/**
 * Run a test body expecting an exception of the
 * given class and with the given message.
 *
 * @param test              To be executed and is expected to throw the exception.
 * @param expectedException The type of the expected exception.
 * @param expectedMessage   If not null, should be the message of the expected exception.
 * @param expectedCause     If not null, should be the same as the cause of the received exception.
 */
public static void expectException(
        Runnable test,
        Class<? extends Throwable> expectedException,
        String expectedMessage,
        Throwable expectedCause) {
    try {
        test.run();
    }
    catch (Exception ex) {
        assertSame(expectedException, ex.getClass());
        if (expectedMessage != null) {
            assertEquals(expectedMessage, ex.getMessage());
        }

        if (expectedCause != null) {
            assertSame(expectedCause, ex.getCause());
        }

        return;
    }

    fail("Didn't find expected exception of type " + expectedException.getName());
}

Затем тестовый код может вызвать это следующим образом:

TestHelper.expectException(
        new Runnable() {
            public void run() {
                classInstanceBeingTested.methodThatThrows();
            }
        },
        WrapperException.class,
        "Exception Message",
        causeException
);
0 голосов
/ 17 ноября 2017

Для JUnit 5 это намного проще:

    @Test
    void testAppleIsSweetAndRed() throws Exception {

        IllegalArgumentException ex = assertThrows(
                IllegalArgumentException.class,
                () -> testClass.appleIsSweetAndRed("orange", "red", "sweet"));

        assertEquals("this is the exception message", ex.getMessage());
        assertEquals(NullPointerException.class, ex.getCause().getClass());
    }

Возвращая сам объект исключения, assertThrows() позволяет вам проверить каждый аспект, касающийся ваших исключений.

0 голосов
/ 25 октября 2010

Я сделал помощника, похожего на остальные:

public class ExpectExceptionsExecutor {

    private ExpectExceptionsExecutor() {
    }

    public static  void execute(ExpectExceptionsTemplate e) {
        Class<? extends Throwable> aClass = e.getExpectedException();

        try {
            Method method = ExpectExceptionsTemplate.class.getMethod("doInttemplate");
            method.invoke(e);
        } catch (NoSuchMethodException e1) {


            throw new RuntimeException();
        } catch (InvocationTargetException e1) {


            Throwable throwable = e1.getTargetException();
            if (!aClass.isAssignableFrom(throwable.getClass())) {
                //  assert false
                fail("Exception isn't the one expected");
            } else {
                assertTrue("Exception captured ", true);
                return;
            }
            ;


        } catch (IllegalAccessException e1) {
            throw new RuntimeException();
        }

        fail("No exception has been thrown");
    }


}

А шаблон клиент должен реализовать

public interface ExpectExceptionsTemplate<T extends Throwable> {


    /**
     * Specify the type of exception that doInttemplate is expected to throw
     * @return
     */
    Class<T> getExpectedException();


    /**
     * Execute risky code inside this method
     * TODO specify expected exception using an annotation
     */
    public void doInttemplate();

}

И код клиента будет выглядеть примерно так:

@Test
    public void myTest() throws Exception {
        ExpectExceptionsExecutor.execute(new ExpectExceptionsTemplate() {
            @Override
            public Class getExpectedException() {
                return IllegalArgumentException.class;
            }

            @Override
            public void doInttemplate() {
                riskyMethod.doSomething(null);
            }
        });
     }

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...