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);
}
});
}
-
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– Bobby2014年02月07日 10:12:13 +00:00Commented Feb 7, 2014 at 10:12
-
2\$\begingroup\$ FYI, I have done something similar in JDOM and I use it like this. \$\endgroup\$rolfl– rolfl2014年02月07日 11:23:45 +00:00Commented Feb 7, 2014 at 11:23
1 Answer 1
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;