我正在为现有的Java Swing应用程序实现一些测试,这样我就可以安全地重构和扩展代码而不会破坏任何东西。我开始在JUnit中进行一些单元测试,因为这似乎是最简单的入门方式,但现在我的首要任务是创建一些端到端的测试以整体运行应用程序。
我在每个测试中重新启动应用程序,方法是将每个测试方法放在一个单独的测试用例中,并在Ant的fork="yes"
任务中使用junit
选项。但是,我想要作为测试实现的一些用例涉及用户退出应用程序,这导致调用System.exit(0)的方法之一。这被JUnit视为错误:junit.framework.AssertionFailedError: Forked Java VM exited abnormally
。
有没有办法告诉JUnit,返回代码为零的退出实际上是否正常?
答案 0 :(得分:27)
库System Rules有一个名为ExpectedSystemExit的JUnit规则。使用此规则,您可以测试调用System.exit(...)的代码:
public class MyTest {
@Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
@Test
public void systemExitWithArbitraryStatusCode() {
exit.expectSystemExit();
/* the code under test, which calls System.exit(...)
* with an arbitrary status
*/
}
@Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
//the code under test, which calls System.exit(0)
}
}
系统规则至少需要JUnit 4.9。
完全披露:我是系统规则的作者。
答案 1 :(得分:8)
我如何处理这个问题是安装一个在调用System.exit时抛出异常的安全管理器。然后有代码捕获异常并且不会使测试失败。
public class NoExitSecurityManager
extends java.rmi.RMISecurityManager
{
private final SecurityManager parent;
public NoExitSecurityManager(final SecurityManager manager)
{
parent = manager;
}
public void checkExit(int status)
{
throw new AttemptToExitException(status);
}
public void checkPermission(Permission perm)
{
}
}
然后在代码中,例如:
catch(final Throwable ex)
{
final Throwable cause;
if(ex.getCause() == null)
{
cause = ex;
}
else
{
cause = ex.getCause();
}
if(cause instanceof AttemptToExitException)
{
status = ((AttemptToExitException)cause).getStatus();
}
else
{
throw cause;
}
}
assertEquals("System.exit must be called with the value of " + expectedStatus, expectedStatus, status);
答案 2 :(得分:6)
你能否将“系统退出”抽象成一个新的依赖项,这样在你的测试中你可能只有一个假记录了一个事实,即已经调用了exit(和值),但是使用了一个调用{ {1}}在真正的应用程序中?
答案 3 :(得分:1)
如果有人需要JUnit 5的此功能,我将written an extension进行此操作。这是一个简单的批注,您可以用来告诉测试用例期望和退出状态代码或特定的退出状态代码。
例如,任何退出代码都可以:
waitForSelector
如果我们要查找特定的代码:
public class MyTestCases {
@Test
@ExpectSystemExit
public void thatSystemExitIsCalled() {
System.exit(1);
}
}