views:

1029

answers:

7

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

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
);
Iain
Is there any way to compress the in-line class / method in Java?
Iain
+11  A: 

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

furtelwart
in this way you must extend the test runner in order to take in account the @ExpectedException
dfa
That is definitely a good answer. Very readable, which IMO is one of the properties of well written code
Edison Gustavo Muenz
Edison: I haven't written the Annotation yet ;-)
furtelwart
+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.
Iain
+6  A: 

For JUNIT 3.x

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

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.

Esko
+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.
Iain
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.
Esko
+2  A: 

i did something very simple

testBla(){
    try {
        someFailingMethod()
        fail(); //method provided by junit
    } catch(Exception e) {
          //do nothing
    }
}
Andreas Petersson
Why don't you do the following? try { someFailingMethod(); fail(); } catch(Exception e) { //nothing } It's more readable
furtelwart
true, did just that, just remembered it wrong..
Andreas Petersson
Anything more complex is pretty pointless.
soru
+4  A: 

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

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)

jaime