4
\$\begingroup\$

In JUnit 3, I cannot tell JUnit what type of exception is expected. In JUnit 4, the declaration @Test(expected=NullPointerException.class) does it.

But, unfortunately in Android development, I can use only JUnit 3. So I wrote this simple method:

public class TestingUtils {

    public static void assertExpectedException(
                           Class<? extends Exception> exceptionClass, Runnable runnable) {
        try {
            runnable.run();
            throw new AssertionFailedError(
                         "Expected exception: <" + exceptionClass.getName() + ">");
        } catch (Exception e) {
            if (!exceptionClass.equals(e.getClass())) {
                throw new AssertionFailedError(
                        "Expected exception: <" + exceptionClass.getName() + "> " +
                        "but was: <" + e.getClass().getName() + ">"
                );
            }
        }
    }
}

Using:

public void testConstructorThrowsExceptionIfArgumentIsNull() {
    TestingUtils.assertExpectedException(IllegalArgumentException.class, new Runnable() {
        @Override
        public void run() {
            new SomeTestedClass(null);
        }
    });
}
\$\endgroup\$
2
  • 2
    \$\begingroup\$ The message for the case if it is was not thrown should be clearer, including something like "Expected exception X, but was not thrown". \$\endgroup\$
    – Bobby
    Feb 7, 2014 at 10:12
  • 2
    \$\begingroup\$ FYI, I have done something similar in JDOM and I use it like this. \$\endgroup\$
    – rolfl
    Feb 7, 2014 at 11:23

1 Answer 1

5
\$\begingroup\$

I have been though this loop before. I chose to do things in a slightly different way to you, in that instead of creating a Runnable, I instead add a try/catch block to the actual test.

Each system has pros/cons, but the differences are really cosmetic. Using a Runnable does make the JUnit logic better contained in the utility code.... but makes calling the utility test code a little bit more complicated. I am not sure which one I prefer.

I have a suggestion regarding this line here:

if (!exceptionClass.equals(e.getClass())) { ....

It is useful to be able to expect a super-type of an exception. For example, you want to trap an SQLException when you run a query, or you want to trap an IOException when you do something on a Stream..... you have the code:

TestingUtils.assertExpectedException(SQLException.class, ....);

but, the actual exception may be com.ibm.db2.jdbc.xxx.PicnicException, which is a subclass of SQLException.

Using equals() to match the exceptions is a problem.... you should use:

if (!exceptionClass.isInstance(e)) {....

One other thing, which is android specific.....

I found a bug in the Android Dalvik implementation/SDK relating to this problem... you should be setting the 'cause' for your AssertionError so that it is logged correctly. Unfortunately, the bug (which is fixed in Jelly-Bean) makes this impossible.

If you are sure you will be testing on Jelly-Bean or newer (the bug was fixed a while ago), you should also be initializing the cause on your AssertionError:

            AssertionFailedError tothrow = new AssertionFailedError(
                    "Expected exception: <" + exceptionClass.getName() + "> " +
                    "but was: <" + e.getClass().getName() + ">"
            );
            tothrow.initCause(e);
            throw tothrow;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.