« Small World | Main | Java String Tricks »

JUnit Exception Handling

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.

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.

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.

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);

What about a simple :

try {
// code which should throw TheException
}
catch (TheException e) {
return; // Test is successful
}
fail("Expected to catch an exception");

Post a comment


About

This page contains a single entry from the blog posted on January 30, 2003 5:05 AM.

The previous post in this blog was Small World.

The next post in this blog is Java String Tricks.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.33