在终止的代码上使用JUnit

时间:2013-04-13 16:58:13

标签: java junit junit4

我正在尝试测试给定的java应用程序,为此我想使用JUnit。

我面临的问题如下:一旦我尝试测试的代码完成其工作,它的调用System.exit()将关闭应用程序。虽然它也阻止我的测试完成,因为它关闭了JVM(我假设)。

有没有解决这个问题,而不修改原始代码?最初我尝试从新线程启动应用程序测试,虽然这显然没有太大的区别。

3 个答案:

答案 0 :(得分:7)

您可以使用System Rules:“JUnit规则集合,用于测试使用java.lang.System的代码。”

在他们的规则中,您有ExpectedSystemExit,下面是如何使用它的示例。我相信这是一个非常干净的解决方案。

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

}

如果你使用maven,可以将其添加到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>

答案 1 :(得分:2)

System.exit(status)实际上将调用委托给 Runtime 类。在继续执行此关闭请求之前,运行时会在JVM的当前 SecurityManager 上调用checkExit(status),这可以通过抛出 SecurityException 来阻止即将发生的关闭。 / p>

通常, SecurityManager 需要确定当前线程是否具有当前安全策略所定义的关闭权限,但由于我们只需要从此退出调用中恢复,我们只需抛出一个我们现在必须在JUnit测试用例中捕获 SecurityException

在JUnit测试类中,使用setUP()方法设置 SecurityManager

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

tearDown()中,使用之前保存的实例再次替换 SecurityManager 。如果不这样做会阻止JUnit立即关闭! :)

参考文献:
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)

  

SecurityManager类包含许多名称以单词check开头的方法。在这些方法执行某些可能敏感的操作之前,这些方法由Java库中的各种方法调用。调用这种检查方法通常如下所示:

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

安全管理员因此有机会通过抛出异常来阻止操作的完成。如果允许操作,安全管理器例程将简单地返回,但如果不允许操作,则抛出SecurityException。

答案 2 :(得分:-2)

除了调用应用程序作为单独的进程(在JVM之外)运行之外,没有办法绕过System.exit()。

您可以从单元测试中执行此操作,并观察从其返回的错误级别。是否能给出足够的反馈通过测试,取决于你的判断。