我正在尝试测试给定的java应用程序,为此我想使用JUnit。
我面临的问题如下:一旦我尝试测试的代码完成其工作,它的调用System.exit()
将关闭应用程序。虽然它也阻止我的测试完成,因为它关闭了JVM(我假设)。
有没有解决这个问题,而不修改原始代码?最初我尝试从新线程启动应用程序测试,虽然这显然没有太大的区别。
答案 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()。
您可以从单元测试中执行此操作,并观察从其返回的错误级别。是否能给出足够的反馈通过测试,取决于你的判断。