Yesterday I came across an interesting phenomena with JUnit.
Consider the following piece of code from TestCase.java:
/**
* Runs the bare test sequence.
* @exception Throwable if any exception is thrown
*/
public void runBare() throws Throwable {
setUp();
try {
runTest();
}
finally {
tearDown();
}
}
All seems quite good - we setUp() the test, run it, and tearDown() the test, even if the test throws an exception.
What happens though if setUp() throws an Exception? or there's a call to fail() inside of setUp()? Well, the test bails as it should with that Exception/error and you get a stack trace. The interesting thing however, is that tearDown() is not called.
Why can this a problem? Check the following:
public abstract class AbstractServerTestCase extends TestCase {
protected void setUp() throws Exception {
super.setUp();
createServer();
}
protected void tearDown() throws Exception {
destroyServer();
super.tearDown();
}
private void createServer() { ... }
private void destroyServer() { ... }
// more code
}
public class ServerProtocolTestCase extends AbstractServerTestCase {
protected void setUp() throws Exception {
super.setUp();
inputData = readResource("./data/input.bin");
}
private byte[] readResource(String location) {
// some code that unfortunately throws an exception or calls fail();
}
private byte[] inputData;
// more code
}
See the potential problem? The TestCase subclass throws an exception or fails(), causing JUnit to unwind it's execution and report the error, but the 'server' isn't shut down since it's in the tearDown() method. In your test cases are all running in the same VM, when the next test case runs, it will report something like a 'failed to bind' message, because it can't start up another server since the port is occupied.
Something to be aware of if you're using intermediate abstract classes to prime your test cases with data. :)
How did we resolve this? well, obviously setUp()'s should be working all the time (although we all know in reality this sometimes isn't the case), so we fixed this, but the main problem was that the failure of one test (the setUp() error), influenced the next one (with a bind or similar exception) when there wasn't something really wrong. To help us resolve this we added a guard to ensure that our server was shutdown even if a subclassed setUp() method threw an exception. Now, we're happy campers again :)
Posted by crafterm at October 29, 2004 01:23 PM | TrackBackNice one, MC !
Greetings,
Olaf
Hey Marcus,
interesting, but honestly wouldn't it make more sense to contact the authors and ask them to explain or change the code?
And in the meantime overwrite testBare(), don't call super.testBare() and copy over the existing code, with the only change that the call to setUp() needs to end up in the try-block too?!
Cheers,
Mariano
Just found a bug reporting regarding this. Unfortunately I don't understand what Kent Beck is saying ...
http://sourceforge.net/tracker/index.php?func=detail&aid=451703&group_id=15278&atid=115278
Posted by: Mariano Kamp at September 9, 2005 11:21 PM