Emulating ExpectedException with the command pattern

The code for this tutorial can be found here.

jUnit4 offers the pretty nifty class ExpectedException. By using the @Rule annotation, this class allows us to write short unit tests that expect a certain exception type to be thrown, and exactly that exception type! For example, suppose you have a class Container which takes an int argument in its constructor, providing the original size of the container. If the user supplies a negative integer, we want to throw an instance of IllegalArgumentException.

Here is how ExpectedException can help us:

@Rule
public ExpectedException thrown= ExpectedException.none();
 
@Test
public void ensureBadArgumentThrows(){
    thrown.expect(IllegalArgumentException.class);
    new Container(-1);   // Should throw IllegalArgumentException
}

I recently had to work in an environment where jUnit, and in particular Hamcrest, had a largely broken installation. This means that I had to write my own testing routines and eschew the built-in jUnit methods. This included situations such as this.

A reasonable solution is based on a trycatch block such as this:

Throwable t = null;
try {
    methodThatShouldThrowAnIllegalArgumentException();
} catch(Throwable tThrown){
   t = tThrown;
} 
if(t == null || t.getClass() != IllegalArgumentException.class)
     System.out.println("Method did not throw expected exception!");

So let’s try to package this into a method of our own. We clearly need references to both the actual subclass of Throwable that we want, as well as the method to execute in order for the particular Throwable that we want to be thrown. This had me stumped at first, since I had never worked around with method references in Java.

No matter; we can do better. We will leverage the command pattern. Within our Main class, we can define an interface called Thrower, with a simple, void, arg-free method called throwIt():

private interface Thrower {
    void throwIt();
}

Now suppose that we only have two types of exceptions that we expect client code to throw, namely an ArrayIndexOutOfBoundsException, and a NullPointerExceptionFor both exceptions, we will create a small class which will extend Thrower and override throwIt() appropriately:

private static class ThrowsArrayIndexOutOfBoundsException implements  Thrower {
    @Override
    public void throwIt() {
        Object[] array = new Object[2];
        Object temp = array[3]; // ArrayIndexOutOfBoundsException thrown.
    }
}
 
private static class ThrowsNullPointerException implements  Thrower {
    @Override
    public void throwIt() {
        Object o = null;
        o.hashCode();   // NullPointerException thrown.
    }
}

It’s almost like we are using Thrower as a functional interface! We can now package the code snippet shared above in a method, which we will call expectThrowable():

private static boolean expectThrowable(Class<?> expectedClass, Thrower methodThatThrows) {
    Throwable exc = null;
    try {
        methodThatThrows.throwIt();
    } catch(Throwable thrown){
        exc = thrown;
    }
    return (exc != null && exc.getClass() == expectedClass);
}

Naturally, we could also make this method void and have it throw some other exception if our conditions are not met, but for simplicity let’s leave it as a boolean method for this example.

Some example calls to this method:

public static void main(String[] args) {
    System.out.println(expectThrowable(ArrayIndexOutOfBoundsException.class, new ThrowsArrayIndexOutOfBoundsException())); // true
    System.out.println(expectThrowable(AssertionError.class, new ThrowsArrayIndexOutOfBoundsException())); // false
    System.out.println(expectThrowable(NullPointerException.class, new ThrowsNullPointerException())); // true
    System.out.println(expectThrowable(AssertionError.class, new ThrowsNullPointerException())); // false
    System.out.println(expectThrowable(Throwable.class, new ThrowsNullPointerException())); // false, too generic
    System.out.println(expectThrowable(RuntimeException.class, new ThrowsNullPointerException())); // false, also too generic
}

So there you have it, one way to emulate ExpectedException with your own code. The way I see it, the bottlenecks here are the JVM’s set-up of the trycatch block and the upcastings in the main() method (late binding is slow, but it’s all we have in Java). Neither are avoidable even in the implementation of ExpectedException.

Previous
Previous

Proper shutdown of a ScheduledExecutorService

Next
Next

Functional Rationals in Scala