我有一些方法,应该在某些输入上调用System.exit()。不幸的是,测试这些用例会导致JUnit终止!将方法调用放在新线程中似乎没有帮助,因为System.exit()终止JVM,而不仅仅是当前线程。有什么共同的模式来处理这个问题吗?例如,我可以用存根代替System.exit()吗?
[编辑]所讨论的类实际上是一个命令行工具,我试图在JUnit中测试它。也许JUnit根本不是适合这项工作的工具?欢迎对补充回归测试工具提出建议(最好能与JUnit和EclEmma很好地集成)。
我有一些方法,应该在某些输入上调用System.exit()。不幸的是,测试这些用例会导致JUnit终止!将方法调用放在新线程中似乎没有帮助,因为System.exit()终止JVM,而不仅仅是当前线程。有什么共同的模式来处理这个问题吗?例如,我可以用存根代替System.exit()吗?
[编辑]所讨论的类实际上是一个命令行工具,我试图在JUnit中测试它。也许JUnit根本不是适合这项工作的工具?欢迎对补充回归测试工具提出建议(最好能与JUnit和EclEmma很好地集成)。
当前回答
可以使用java SecurityManager防止当前线程关闭java虚拟机。下面的代码应该是你想要的:
SecurityManager securityManager = new SecurityManager() {
public void checkPermission(Permission permission) {
if ("exitVM".equals(permission.getName())) {
throw new SecurityException("System.exit attempted and blocked.");
}
}
};
System.setSecurityManager(securityManager);
其他回答
使用运行时。exec(字符串命令)在单独的进程中启动JVM。
SecurityManager解决方案有一个小问题。一些方法,如JFrame。exitOnClose,也调用SecurityManager.checkExit。在我的应用程序中,我不希望这个调用失败,所以我使用了
Class[] stack = getClassContext();
if (stack[1] != JFrame.class && !okToExit) throw new ExitException();
super.checkExit(status);
如何注入一个“ExitManager”到这个方法:
public interface ExitManager {
void exit(int exitCode);
}
public class ExitManagerImpl implements ExitManager {
public void exit(int exitCode) {
System.exit(exitCode);
}
}
public class ExitManagerMock implements ExitManager {
public bool exitWasCalled;
public int exitCode;
public void exit(int exitCode) {
exitWasCalled = true;
this.exitCode = exitCode;
}
}
public class MethodsCallExit {
public void CallsExit(ExitManager exitManager) {
// whatever
if (foo) {
exitManager.exit(42);
}
// whatever
}
}
生产代码使用ExitManagerImpl,测试代码使用ExitManagerMock,可以检查是否调用了exit()以及使用哪个退出代码。
实际上,您可以模拟或排除该系统。exit方法,在JUnit测试中。
例如,使用JMockit你可以这样写(也有其他方法):
@Test
public void mockSystemExit(@Mocked("exit") System mockSystem)
{
// Called by code under test:
System.exit(); // will not exit the program
}
编辑:替代测试(使用最新的JMockit API),它不允许任何代码在调用System.exit(n)后运行:
@Test(expected = EOFException.class)
public void checkingForSystemExitWhileNotAllowingCodeToContinueToRun() {
new Expectations(System.class) {{ System.exit(anyInt); result = new EOFException(); }};
// From the code under test:
System.exit(1);
System.out.println("This will never run (and not exit either)");
}
系统存根(https://github.com/webcompere/system-stubs)也能够解决这个问题。它共享System Lambda的语法,用于包装我们知道将执行System的代码。退出,但当其他代码意外退出时,可能会导致奇怪的效果。
通过JUnit 5插件,我们可以保证任何退出都将转换为异常:
@ExtendWith(SystemStubsExtension.class)
class SystemExitUseCase {
// the presence of this in the test means System.exit becomes an exception
@SystemStub
private SystemExit systemExit;
@Test
void doSomethingThatAccidentallyCallsSystemExit() {
// this test would have stopped the JVM, now it ends in `AbortExecutionException`
// System.exit(1);
}
@Test
void canCatchSystemExit() {
assertThatThrownBy(() -> System.exit(1))
.isInstanceOf(AbortExecutionException.class);
assertThat(systemExit.getExitCode()).isEqualTo(1);
}
}
或者,类似断言的静态方法也可以使用:
assertThat(catchSystemExit(() -> {
//the code under test
System.exit(123);
})).isEqualTo(123);