How to test System.exit

From EggeWiki

One might assume that testing a console app method which calls System.exit() would be impossible to do. By creating a SecurityManager we can detect and defer this call.

import java.io.IOException;
import java.security.Permission;
import java.util.Stack;

public class ConsoleAppUnitTest extends UnitTestCase {
    private SecurityManager existingSecurityManager;

    @Override
    protected void setUp() throws Exception {
        existingSecurityManager = System.getSecurityManager();
    }

    @Override
    protected void tearDown() throws Exception {
        System.setSecurityManager(existingSecurityManager);
    }

    private static class ExitCatchingSecurityManager extends SecurityManager {
        private Stack<Integer> exitCodes = new Stack<Integer>();

        public void checkExit(int status) {
            exitCodes.push(status);
            throw new SecurityException();
        }

        /**
         * This method is called before the original security manager can be replaced.  If this method does
         * not throw and exception then 'System.setSecurityManager(existingSecurityManager)' will work.
         * @param perm anything
         */
        @Override
        public void checkPermission(Permission perm) {
        }

        public int getLastExit() {
            return exitCodes.pop();
        }
    }

    public void testNoArgsExitsWithReturnCode() {
        ExitCatchingSecurityManager securityManager = new ExitCatchingSecurityManager();
        try {
            System.setSecurityManager(securityManager);
            ConsoleApp.main(new String[] { });
            fail();
        } catch (SecurityException e) {
            pass();
            assertEquals(2, securityManager.getLastExit());
            assertEquals(1, securityManager.getLastExit());
        }
    }
}

System.exit is called twice in this case, because the first System.exit throws an SecurityException which is caught in the 'catch Throwable' line. This trys to call System.exit again, and ends up throwing another exception.

    @SuppressWarnings({ "CallToSystemExit" })
    public static void main(String[] args) {
        try {
            if (args.length != 2) {
                System.err.println("Usage: inputFile outputFile");
                System.exit(1);
            }
            new ConsoleApp(args[0], args[1]).run();
        } catch (Throwable t) {
            t.printStackTrace(System.err);
            System.exit(2);
        }
    }