Here's the scenario: a unit test throws an uncaught exception, but then so does tearDown()
. What happens? TestCase.runBare()
says:
public void runBare() throws Throwable { setUp(); try { runTest(); } finally { tearDown(); } }
See the problem? If tearDown()
throws, then runBare()
loses any exception thrown by runTest()
(which runs your unit test). [See the Java Language Specification, §11.3.] Rarely is this what you actually want since the lost exception was the original cause of test error. The solution—override runBare()
:
public void runBare() throws Throwable { Throwable thrown = null; setUp(); try { runTest(); } catch (final Throwable t) { thrown = t; } finally { try { tearDown(); if (null != thrown) throw thrown; } catch (Throwable t) { if (null != thrown) throw thrown; else throw t; } } }
The idea is simple. Store the exception from runTest()
and rethrow it after running tearDown()
. Make sure to throw any exception from tearDown()
otherwise.
UPDATE: My brain finally caught up with my fingers and I realize that the sample solution is unnecessarily complex. Better is:
public void runBare() throws Throwable { Throwable thrown = null; setUp(); try { runTest(); } catch (final Throwable t) { thrown = t; } finally { try { tearDown(); } finally { if (null != thrown) throw thrown; } } }
2 comments:
why (null != thrown) and not (thrown != null)?
Why? I just like the keyword first for aesthetic reasons. I also find it slightly easier to pick out when scanning vertically down a long listing of code.
I would have been better off with a slightly different idiom:
try {
tearDown();
} finally {
if (null != thrown) throw thrown;
}
And now I am relying on the fact that the earlier exception is swallowed by the JVM when the latter throws. Making lemondade, as it were.
Post a Comment