Download

JUnit Contrib - AssertThrows

Testing for Expected Exceptions

This JUnit extension allows you to test for exceptions as you would test for other conditions, often as:

assertThrows(emptyList).get(0);

Contents

This is an alpha release. The package names, API, and features may change in a future release.

The Problem

Traditionally, there are two ways to verify that code throws exceptions as expected: using an annotation, and using try-fail-catch. However both are more complex than using assertTrue or assertEquals. This project offers two additional, sometimes simpler ways to test for exceptions: using assertThrows() and using new AssertThrows().

Annotation

If the test method itself is expected to throw an IndexOutOfBoundsException, use the following annotation as described in JUnit FAQ:

@Test(expected=IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    List<String> emptyList = new ArrayList<String>();
    emptyList.get(0);
}

Try-Fail-Catch

If the test is more complex and doesn't end where the exception is expected, you could write:

List<String> emptyList = new ArrayList<String>();
try {
    emptyList.get(0);
    fail();
} catch (IndexOutOfBoundsException e) {
    // expected
}

This will allow you to verify the exception message as well. However, it is very easy to forget the fail().

Proxy

In many cases, this project allows you to simplify such tests to:

List<String> emptyList = new ArrayList<String>();
assertThrows(emptyList).get(0);

Please note the somewhat unusual order of brackets. To verify the correct exception is thrown, use:

assertThrows(IndexOutOfBoundsException.class, emptyList).get(0);

To verify the exception message as well, use:

assertThrows(new IndexOutOfBoundsException(
    "Index: 0, Size: 0"), emptyList).
    get(0);

Please note you need to add the following static import statement:

import static org.junit.contrib.assertthrows.AssertThrows.assertThrows;

Internally, the assertThrows creates a proxy for the emptyList object, which verifies all method calls to this object throw an exception.

Limitations and Class Proxies

Static methods and constructors can't be tested in this way. Testing the finalize method is not supported. This solution works best with interfaces (as in this example the List interface). By default, assertThrows will try to create a proxy using the java.lang.reflect.Proxy mechanism. For classes that do not implement an interface, and to test methods that are not part of any interface, a class proxy is required (a proxy that extends a class, and not just implements interfaces). Because this is tricky, assertThrows doesn't do that by default. To explicitly use a class proxy, call AssertThrows.useClassProxy. As an example, if you declare emptyList to be an instance of the concrete class ArrayList, you need to tell the tool to use a class proxy:

ArrayList<String> emptyList = new ArrayList<String>();
AssertThrows.useClassProxy(ArrayList.class);
assertThrows(emptyList).get(0);

Once such a class proxy is created, it is used for all future assertThrows calls where the provided object is of this class. Creating a proxy that extends a class is a bit challenging. It is not possibly to create a proxy for a final class, because extending a final class is not allowed. Also, final methods can not be overridden. Creating a class proxy on a class proxy itself is not supported. To create class proxies, the proxy factory uses the libraries Cglib and Objenesis if they are in the classpath. If not, Java source code for the proxy class is generated, and then compiled using javax.tools.JavaCompiler (Java 6) or com.sun.tools.javac.Main (Java 5). This does have a few limitations: private classes, non-static inner classes, and anonymous inner classes are not supported. Also, classes without public constructors are not supported. Most of those limitations do not apply when using Cglib and Objenesis however.

Calling a Final Method, and Forgetting to Call a Method

A final method can not be overridden, and therefore the tool can not verify whether it threw an exception. However, such cases are detected on the next usage (the next call to assertThrows, and the next time an AssertThrows object is created), or when the proxy is garbage collected. So if you made a mistake and called a final method, or did not call method on the proxy assertThrows object, the tool will usually detect it. As an example, the following code will throw an exception saying that no method was called on the proxy:

List<String> list = new ArrayList<String>();
assertThrows(list);
assertThrows(list).get(0);

You may also verify the usage explicitly by calling ExceptionVerifier.verifyLastProxyWasUsed(), for example in a tearDown method or JUnit 4 Rule.

Anonymous Class

The following syntax is supported:

new AssertThrows() { public void test() {
    Integer.parseInt("x");
}};

The constructor of this class calls test() and verifies it throws an exception. To test for a specific exception, use:

new AssertThrows(NumberFormatException.class) { public void test() {
    Integer.parseInt("x");
}};

You may also test for a specific exception message using:

new AssertThrows(new NumberFormatException("For input string: \"x\"")) {
    public void test() {
        Integer.parseInt("x");
}};

Please note you need to add the following import statement:

import org.junit.contrib.assertthrows.AssertThrows;

Getting the Last Throwable

If you need to verify the exception in more detail (for example, match the message against a pattern, or verify the cause), you can get the last exception or error as follows:

Throwable t = AssertThrows.getLastThrown();

This will get the last exception or error that was thrown by a test in the current thread, no matter if the proxy mechanism (assertThrows) or the anonymous class mechanism (new AssertThrows) was used. If the last executed test did not throw an exception, it will return null. Please note even thought this is a static method, it is multi-threading save because it uses a ThreadLocal variable internally.

Usage

To use this tool, download add the .jar file to your project. A minimal test case is:

import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.contrib.assertthrows.AssertThrows;
import static org.junit.contrib.assertthrows.AssertThrows.*;
public class HelloAssertThrows {
    @Test
    public void test() {
        final List<String> emptyList = new ArrayList<String>();
        assertThrows(emptyList).get(0);
        new AssertThrows() { public void test() {
            emptyList.get(0);
        }};
    }
}

Using Maven

The jar file is not yet in a Maven central repository, but you can build and deploy it locally if you wish. To do that, first download the .zip or .tar.gz file, and build it using:

cd assertthrows
mvn clean install

Then use the following dependency:

<dependency>
    <groupId>org.junit.contrib</groupId>
    <artifactId>junit-assertthrows</artifactId>
    <version>0.1-SNAPSHOT</version>
    <scope>test</scope>
</dependency>

Download and Links

For bugs and feature requests, please use the issue tracker.

To clone the project with Git, run:

git clone git://github.com/dsaff/junit.contrib

Dependencies

This project does not required additional libraries. However, if you want to test using class proxies, it is suggested to add the libraries Cglib and Objenesis to the classpath while running the tests. Please note those are optional dependencies only; within the described limitations, this tool works without them. To add the dependencies to a Maven project, use:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>2.2.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>1.2</version>
    <scope>test</scope>
</dependency>

License

This project uses the Apache License Version 2.0.

Known Problems

There are classpath problems when using Maven 2.x. together with the the compiling proxy factory. If the compiling proxy factory is required, you will need to use Maven 3.x.