by BehindJava

How do you assert that a certain exception is thrown in JUnit 4 tests

Home » java » How do you assert that a certain exception is thrown in JUnit 4 tests

In this tutorial we are going to learn about asserting a certain exception is thrown in JUnit 4 tests.

In junit, there are four ways to test exception.

junit5.x

For junit5.x, you can use assertThrows as following

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
    assertEquals("expected messages", exception.getMessage());
}

junit4.x

For junit4.x, use the optional ‘expected’ attribute of Test annonation

@Test(expected = IndexOutOfBoundsException.class)
public void testFooThrowsIndexOutOfBoundsException() {
    foo.doStuff();
}

For junit4.x, use the ExpectedException rule

public class XxxTest {
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        thrown.expect(IndexOutOfBoundsException.class)
        //you can test the exception message like
        thrown.expectMessage("expected messages");
        foo.doStuff();
    }
}

you also can use the classic try/catch way widely used under junit 3 framework

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        fail("expected exception was not occured.");
    } catch(IndexOutOfBoundsException e) {
        //if execution reaches here, 
        //it indicates this exception was occured.
        //so we need not handle it.
    }
}

So

  • If you like junit 5, then you should like the 1st one.
  • The 2nd way is used when you only want test the type of exception.
  • The first and last two are used when you want test exception message further.
  • If you use junit 3, then the 4th one is preferred.

For more info, you can read this document and junit5 user guide for details.

Java 8 solution

If you would like a solution which:

  • Utilizes Java 8 lambdas
  • Does not depend on any JUnit magic
  • Allows you to check for multiple exceptions within a single test method
  • Checks for an exception being thrown by a specific set of lines within your test method instead of any unknown line in the entire test method.
  • Yields the actual exception object that was thrown so that you can further examine it

Here is a utility function:

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 testing for "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.
}

Use it as follows:

@Test
public void testMyFunction()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            myFunction();
        } );
    assert e.getMessage().equals( "I bj fail!" );
}

public void myFunction()
{
    throw new RuntimeException( "I bj fail!" );
}