Using JUnit on code that terminates

18,682

Solution 1

You can use System Rules: "A collection of JUnit rules for testing code which uses java.lang.System."

Among their rules, you have ExpectedSystemExit, below is an example on how to use it. I believe it is a very clean solution.

import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.Assertion;
import org.junit.contrib.java.lang.system.ExpectedSystemExit;

public class SystemExitTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void noSystemExit() {
        //passes
    }

    @Test
    public void executeSomeCodeAFTERsystemExit() {
        System.out.println("This is executed before everything.");
        exit.expectSystemExit();
        exit.checkAssertionAfterwards(new Assertion() {
            @Override
            public void checkAssertion() throws Exception {
                System.out.println("This is executed AFTER System.exit()"+
                " and, if exists, the @org.junit.After annotated method!");
            }
        });
        System.out.println("This is executed right before System.exit().");
        System.exit(0);
        System.out.println("This is NEVER executed.");
    }

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        System.exit(0);
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        System.exit(0);
    }

    @Test
    public void failSystemExit() {
        exit.expectSystemExit();
        //System.exit(0);
    }

}

If you use maven, you can add this to your pom.xml:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
</dependency>
<dependency>
    <groupId>com.github.stefanbirkner</groupId>
    <artifactId>system-rules</artifactId>
    <version>1.3.0</version>
</dependency>

Solution 2

System.exit(status) actually delegates the call to Runtime class. Runtime before proceeding with this shutdown request invokes checkExit(status) on JVM's current SecurityManager which can prevent the impending shutdown by throwing a SecurityException.

Usually, the SecurityManager needs to establish if the current thread has the privilege to shutdown defined by the current security policy in place but since all we need is to recover from this exit call we simply throw a SecurityException that we'll now have to catch in our JUnit test case.

In your JUnit test class, setup a SecurityManager in setUP() method:

    securityManager = System.getSecurityManager();
    System.setSecurityManager(new SecurityManager() {
        @Override
        public void checkExit(int status) {
            super.checkExit(status); // This is IMPORTANT!
            throw new SecurityException("Overriding shutdown...");
        }
    });

In tearDown() replace the SecurityManager again with the instance that we saved before. Failure to do so would prevent JUnit from shutting down now! :)

References:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/SecurityManager.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/SecurityManager.html#checkExit(int)

The SecurityManager class contains many methods with names that begin with the word check. These methods are called by various methods in the Java libraries before those methods perform certain potentially sensitive operations. The invocation of such a check method typically looks like this:

     SecurityManager security = System.getSecurityManager();
     if (security != null) {
         security.checkXXX(argument,  . . . );
     }

The security manager is thereby given an opportunity to prevent completion of the operation by throwing an exception. A security manager routine simply returns if the operation is permitted, but throws a SecurityException if the operation is not permitted.

Share:
18,682
Giannis
Author by

Giannis

Junior Dev..

Updated on June 26, 2022

Comments

  • Giannis
    Giannis almost 2 years

    I am trying to test a given java application, and for that purpose I want to use JUnit.

    The problem I am facing is the following: Once the code I am trying to test finishes its work, its calling System.exit(), which closes the application. Although it is also stoping my tests from completing, as it closes the JVM (I assume).

    Is there anyway to go around this problem, without modifying the original code? Initially I tried launching the application im testing from new thread, although that obviously didn't make much difference.