30

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?

2
  • I would appreciate if you were able to check the best fitting answer as the correct one. Commented Oct 22, 2009 at 11:32
  • 1
    You should not care which method throws the exception: that is an implementation detail. Commented Dec 18, 2015 at 19:48

10 Answers 10

25

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?"); } } 
Sign up to request clarification or add additional context in comments.

1 Comment

I changed the accepted answer to this one because I felt it would take too long to rise to the top and this is probably the most up-to-date. Note I don't work on Java on a day-to-day basis at the moment so am not sure how widely this new approach is being adopted.
24

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 :)

4 Comments

in this way you must extend the test runner in order to take in account the @ExpectedException
That is definitely a good answer. Very readable, which IMO is one of the properties of well written code
Edison: I haven't written the Annotation yet ;-)
+1 This is a very good answer. The only point it doesn't satisfy from the question is 'The exact method call that throws the wrapped exception.' I'm also interested to see the code behind the annotation.
19

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

2 Comments

This on its own is not a good practice - your test will also pass if no exception is actually thrown. If you are going to do a try/catch, then you should have something like assertFalse(true); in the try block after your fail method to ensure you never actually get to the next line
Yes, and fail(...) is the same as assertFalse(true)
11

For JUNIT 3.x

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

Comments

5

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.

2 Comments

+1 I like this. It gives you a standard set of validation along with a mechanism to extend that validation on a per-test basis. I think it might be more readable if the functor class was defined inline.
I thought this a bit more, some of the code can be gotten rid of with abstract classes which implement fe. the type and validate methods so that one can just write assertThrowsException(new ThrowNPE() { public void invoke() {String s = null;s.charAt(null);}}); Still seems a bit heavy to me but then again, this may help asserting complex exceptions while the other approach is better for simpler cases.
5

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

Comments

2

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

1 Comment

Is there any way to compress the in-line class / method in Java?
2

i did something very simple

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

1 Comment

Why don't you do the following? try { someFailingMethod(); fail(); } catch(Exception e) { //nothing } It's more readable
2

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.

1 Comment

This should be the accepted answer, because JUnit 5 was released back in 2017, so it should be the standard version to use for all new projects going forward.
0

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)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.