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);
 }
 });
}
rolfl
98.1k17 gold badges219 silver badges419 bronze badges
asked Feb 7, 2014 at 10:06
\$\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\$ Commented Feb 7, 2014 at 10:12
  • 2
    \$\begingroup\$ FYI, I have done something similar in JDOM and I use it like this. \$\endgroup\$ Commented 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;
answered Feb 7, 2014 at 12:38
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.