4

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

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

有没有办法在不修改原始代码的情况下解决这个问题?最初我尝试从新线程启动应用程序即时测试,尽管这显然没有太大区别。

4

3 回答 3

11

您可以使用系统规则:“用于测试代码的 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>
于 2013-04-13T23:14:00.210 回答
3

System.exit(status)实际上将调用委托给Runtime类。继续此关闭请求之前的运行时checkExit(status)调用JVM 的当前SecurityManager可以通过抛出SecurityException来防止即将关闭。

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

在您的 JUnit 测试类中,在方法中设置一个SecurityManagersetUP()

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

再次将SecurityManagertearDown()替换为我们之前保存的实例。不这样做会阻止 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。

于 2013-04-14T01:01:08.637 回答
-3

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

您可以从单元测试中执行此操作,并观察从它返回的错误级别。这是否对通过测试提供足够的反馈取决于您的判断。

于 2013-04-13T17:26:40.010 回答