Java:如何测试调用System.exit()的方法?
问题
我有一些方法应该在某些输入上调用567779278。不幸的是,测试这些情况会导致JUnit终止!将方法调用放在新线程中似乎没有帮助,因为System.exit()
终止了JVM,而不仅仅是当前线程。是否有任何常见的处理方式?例如,我可以将存根替换为System.exit()
吗?
[编辑]有问题的类实际上是一个命令行工具,我试图在JUnit中测试。也许JUnit根本不适合这份工作?建议使用互补回归测试工具(最好是与JUnit和EclEmma完美集成的东西)。
#1 热门回答(184 赞)
事实上,Derkeiler.comsuggests:
- 为什么选择System.exit()?
而不是使用System.exit(whateverValue)终止,为什么不抛出未经检查的异常?在正常使用中,它会一直漂移到JVM的最后一个捕获器并关闭你的脚本(除非你决定在途中捕获它,这可能在某一天有用)。在JUnit场景中,它将被JUnit框架捕获,该框架将报告此类测试失败并顺利地移动到下一个。
- 防止System.exit()实际退出JVM:
尝试修改TestCase以使用安全管理器运行,该管理器阻止调用System.exit,然后捕获SecurityException。
public class NoExitTestCase extends TestCase
{
protected static class ExitException extends SecurityException
{
public final int status;
public ExitException(int status)
{
super("There is no escape!");
this.status = status;
}
}
private static class NoExitSecurityManager extends SecurityManager
{
@Override
public void checkPermission(Permission perm)
{
// allow anything.
}
@Override
public void checkPermission(Permission perm, Object context)
{
// allow anything.
}
@Override
public void checkExit(int status)
{
super.checkExit(status);
throw new ExitException(status);
}
}
@Override
protected void setUp() throws Exception
{
super.setUp();
System.setSecurityManager(new NoExitSecurityManager());
}
@Override
protected void tearDown() throws Exception
{
System.setSecurityManager(null); // or save and restore original
super.tearDown();
}
public void testNoExit() throws Exception
{
System.out.println("Printing works");
}
public void testExit() throws Exception
{
try
{
System.exit(42);
} catch (ExitException e)
{
assertEquals("Exit status", 42, e.status);
}
}
}
2012年12月更新:
Willproposesin the commentsusing系统规则,JUnit(4.9)规则的集合,用于测试使用422516276的代码。
这最初是在2011年12月由3840674905inhis answer提到的。
System.exit(…)
使用ExpectedSystemExit规则验证是否已调用System.exit(...)。你也可以验证退出状态。
例如:
public void MyTest {
@Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
@Test
public void noSystemExit() {
//passes
}
@Test
public void systemExitWithArbitraryStatusCode() {
exit.expectSystemExit();
System.exit(0);
}
@Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
System.exit(0);
}
}
#2 热门回答(73 赞)
librarySystem Rules有一个名为ExpectedSystemExit的JUnit规则。使用此规则,你可以测试调用System.exit(...)的代码:
public void MyTest {
@Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
@Test
public void systemExitWithArbitraryStatusCode() {
exit.expectSystemExit();
//the code under test, which calls System.exit(...);
}
@Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
//the code under test, which calls System.exit(0);
}
}
完全披露:我是该图书馆的作者。
#3 热门回答(30 赞)
你实际上是在JUnit测试中模拟或删除了System.exit
方法。
例如,使用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)");
}