Gherhard Froehlich was wondering if there was a better way to do positive JUnit exception testing than something like this:
public void testAddService() {
try {
setUp();
TRACER.trace("--- Testing addService() ---");
ssgConnectorProvisioning.addService(serviceData);
TRACER.trace("--- End of test ---", "\n");
} catch (Exception e) {
if(e instanceof SSGConnectorException) {
TRACER.exception("Test OK, because Exception was controlled", e);
} else {
TRACER.exception(e);
fail(e.getMessage());
}
}
}There is, the thing to do is to take advantage of the fact that java will only use one exception block, and it will match the first one that is an assignable class of the exception, so you could do this...
public void testAddService() {
try {
setUp();
TRACER.trace("--- Testing addService() ---");
ssgConnectorProvisioning.addService(serviceData);
TRACER.trace("--- End of test ---", "\n");
} catch (SSGConnectorException sce) {
TRACER.exception("Test OK, because Exception was controlled", e);
} catch (Exception e) {
TRACER.exception(e);
fail(e.getMessage());
}
}
Let's hope he get's this via a Moveable Type pingback. (I've wanted to see if I have it working or not).
Update: I was kind of myoptic when I looked at the post Gherhard had and realized it was missing something this morning. Charles Miller also noticed it last night: the test should fail if no exception is thrown. steve tried to trim it down to a single catch:
public void testAddService() throws Exception{
try {
TRACER.trace("--- Testing addService() ---");
ssgConnectorProvisioning.addService(serviceData);
TRACER.trace("--- End of test ---", "\n");
} catch (SSGConnectorException sce) {
TRACER.exception("Test OK, because Exception was controlled", e);
}
fail("We expected a SSGConnectorException");
}
Except in this case the test will fail if the exception is throw! (a false failure). And this also assumes SSGConnectiorException is the only exception thrown. If your tests are written so that only one test is done per test method (a good practice IMHO) you could fix this by putting a return statement in the catch block or moving the fail inside of the try block. Otherwise (such as cases where the exception is just a set up for the real test) you will need two fail statements or catch statements or continue the test in the catch block.
Comments (5)
Firstly, the test is missing a _vital_ element - as written the test will succeed if the method doesn't throw an exception at all. Directly after the method that is supposed to throw the exception, put a 'fail("expected SSGConnectorException");'
Secondly, I tend to prefer just declaring the test method as throwing Exception. That way you don't have to bother with the "catch (Exception e)" block: any exception thrown other than the one you expect will be thrown out of the test method, and cause the test to fail (testrunner will print the stacktrace of the offending exception.
Oh, and thirdly, JUnit calls setUp() automagically before every test (and tearDown()) afterwards, so I'm not sure what the manual call is doing there.
Posted by Charles Miller | January 31, 2003 3:21 AM
Posted on January 31, 2003 03:21
Actually, if I were to write that test my code would read exactly as follows:
public void testAddServiceThrowsException throws Exception {
try {
ssgConnectorProvisioning.addService(serviceData);
fail("Expected SSGConnectorException");
} catch (SSGConnectorException e) {
// expected result
}
}
Tracing inside a test is, I've found, something of a wasted effort. JUnit tells you when you're going into the test. If an exception is thrown, the stacktrace will tell you exactly what line it came from. If the test fails, you can put enough information in the assert() to explain where and why. So long as you pick good test names, and have good descriptions for your assert() statements, tracing just seems redundant.
Posted by Charles Miller | January 31, 2003 4:00 AM
Posted on January 31, 2003 04:00
so the code would read:
public void testAddService() throws Exception{
try {
TRACER.trace("--- Testing addService() ---");
ssgConnectorProvisioning.addService(serviceData);
TRACER.trace("--- End of test ---", "\n");
} catch (SSGConnectorException sce) {
TRACER.exception("Test OK, because Exception was controlled", e);
}
fail("We expected a SSGConnectorException");
}
And if you need a case where the call is successful that's a separate test.
Posted by steve | January 31, 2003 5:11 AM
Posted on January 31, 2003 05:11
A way I usually write these tests is:
TheException caught = null;
try {
// code which should throw TheException
}
catch (TheException e) {
caught = e;
}
assertTrue("Expected to catch an exception",
exception != null);
Posted by Mike Moran | February 1, 2003 7:03 AM
Posted on February 1, 2003 07:03
What about a simple :
try {
// code which should throw TheException
}
catch (TheException e) {
return; // Test is successful
}
fail("Expected to catch an exception");
Posted by Sylvain Wallez | February 5, 2003 2:38 AM
Posted on February 5, 2003 02:38