Showing posts with label tdd. Show all posts
Showing posts with label tdd. Show all posts

Wednesday, September 16, 2009

How to write an automated test for code that calls System.exit()

The other day I came across some code that during certain conditions was expected to call System.exit(). Naturally I wanted test coverage for that code too, but since any test calling the method in question would terminate the entire testrun I had to find a different approach. Here is one strategy that can be used.

Assume we need to write a test for the following simple method. (Yep, I just made this one up.)


    public void inconceivable() {
        System.exit(42);
    }

I can not just call the method in a JUnit test like this (but feel free to try it :-)).

    @Test
    public void shouldCallSystemExit() {
        new ObjectUnderTest().inconceivable();
        // Assert what now?
    }

One way to make it possible is by utilizing a SecurityManager. By installing a custom security manager I can prevent the exit from actually happen. The following implementation overrides checkExit() to throw an exception whenever a call to System.exit() is made. By also overriding checkPermission() to do nothing, I allow myself to remove the security manager again, after it has been installed.

class ExceptionOnExitSecurityManager extends SecurityManager {
    @Override
    public void checkExit(int status) {
        super.checkExit(status);
        throw new SystemExitException(status);
    }

    @Override
    public void checkPermission(Permission perm) {
        // Override to allow removal of this security manager
    }
}

A custom exception makes it really easy to access the status code.

class SystemExitException extends SecurityException {
    int statusCode;
    SystemExitException(int statusCode) {
        this.statusCode = statusCode;
    }
}

Now I am ready to write my test.

    @Test
    public void shouldCallSystemExit() {
        System.setSecurityManager(new ExceptionOnExitSecurityManager());
        try {
            new ObjectUnderTest().inconceivable();
            fail("Did not call System.exit()");
        } catch (SystemExitException e) {
            assertEquals(42, e.statusCode);
        }
        System.setSecurityManager(null);
    }

This test will fail nicely if System.exit() is never called, or if it is called with a different status code than expected. Now the expected behaviour is documented and verified, and my test coverage moved up a notch.

Do you have a different strategy for this type of situations, or do you see any problems with this approach? Please share it in the comments!