Iain
Iain

Reputation: 10749

In Java how can I validate a thrown exception with JUnit?

When writing unit tests for a Java API there may be circumstances where you want to perform more detailed validation of an exception. I.e. more than is offered by the @test annotation offered by JUnit.

For example, consider an class that should catch an exception from some other Interface, wrap that exception and throw the wrapped exception. You may want to verify:

  1. The exact method call that throws the wrapped exception.
  2. That the wrapper exception has the original exception as its cause.
  3. The message of the wrapper exception.

The main point here is that you want to be perf additional validation of an exception in a unit test (not a debate about whether you should verify things like the exception message).

What's a good approach for this?

Upvotes: 30

Views: 42024

Answers (10)

Marcus K.
Marcus K.

Reputation: 1040

For JUnit 5 it is much easier:

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

By returning the exception object itself, assertThrows() allows you to test every aspect regarding your thrown exceptions.

Upvotes: 2

Jonas
Jonas

Reputation: 3020

In JUnit 4 it can be easily done using ExpectedException rule.

Here is example from 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?");
    }
}

Upvotes: 25

rwitzel
rwitzel

Reputation: 1818

@akuhn:

Even without closures we can get a more readable solution (using catch-exception):

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

public void test() {
    ...
    ...
    catchException(nastyBoy).doNastyStuff();
    assertTrue(caughtException() instanceof WhateverException);
    assertEquals("Message", caughtException().getMessage());
}

Upvotes: 5

Jaime Hablutzel
Jaime Hablutzel

Reputation: 6333

I made a helper similar to the other posted ones:

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


}

And the template the client should implement

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

}

And the client code would be something like this:

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

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

It looks really verbose but if you use an IDE with good autocompletion you will only need to write the type of exception and the actual code under test. (the rest will be done by the IDE :D)

Upvotes: 0

guerda
guerda

Reputation: 24049

As provided in your answer, it's a good approach. In addition to this:

You could wrap the function expectException into a new Annotation, called ExpectedException.
An annotated method would look like this:

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

This way would be more readable, but it's exactly the same approach.

Another reason is: I like Annotations :)

Upvotes: 24

Andreas Petersson
Andreas Petersson

Reputation: 16518

i did something very simple

testBla(){
    try {
        someFailingMethod()
        fail(); //method provided by junit
    } catch(Exception e) {
          //do nothing
    }
}

Upvotes: 2

akuhn
akuhn

Reputation: 27793

Looking at the proposed answers, you can really feel the pain of not having closures in Java. IMHO, the most readable solution is ye good old try catch.

@Test
public void test() {
    ...
    ...
    try {
        ...
        fail("No exception caught :(");
    }
    catch (RuntimeException ex) {
        assertEquals(Whatever.class, ex.getCause().getClass());
        assertEquals("Message", ex.getMessage());
    }
}

Upvotes: 19

Esko
Esko

Reputation: 29367

Until this post I've done my exception validation by doing this:

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

I spent a few moments thinking about the issue though and came up with the following (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);

Looking at these two I can't decide which one I like more. I guess this is one of those issues where achieving a goal (in my case, the assertion method with functor parameter) isn't worth it in the long run since it's just a lot easier to do those 6+ of code to assert the try..catch block.

Then again, maybe my 10 minute result of problem solving at friday evening just isn't the most intelligent way to do this.

Upvotes: 5

mP.
mP.

Reputation: 18256

For JUNIT 3.x

public void test(){
   boolean thrown = false;
   try{
      mightThrowEx();
   } catch ( Surprise expected ){
      thrown = true;
      assertEquals( "message", expected.getMessage());
   }
  assertTrue(thrown );
}

Upvotes: 11

Iain
Iain

Reputation: 10749

The following helper method (adapted from this blog post) does the trick:

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

The test code can then invoke this as follows:

TestHelper.expectException(
        new Runnable() {
            public void run() {
                classInstanceBeingTested.methodThatThrows();
            }
        },
        WrapperException.class,
        "Exception Message",
        causeException
);

Upvotes: 2

Related Questions