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