大多数解决方案将
- 在那一刻终止测试(方法,而不是整个运行)
System.exit()
调用
- 忽略已经安装的
SecurityManager
- 有时对测试框架非常特定
- 限制每个测试用例最多使用一次
因此,大多数解决方案都不适合以下情况:
- 副作用应在召集后进行
System.exit()
- 现有的安全管理器是测试的一部分。
- 使用了不同的测试框架。
- 您希望在一个测试用例中进行多个验证。严格不建议这样做,但有时会非常方便
assertAll()
,例如与结合使用时。
我对其他答案中提出的现有解决方案所施加的限制不满意,因此我自己提出了一些建议。
下列类提供一种assertExits(int expectedStatus, Executable executable)
断言System.exit()
具有指定status
值的方法,并且测试可以在此方法之后继续进行。它的工作方式与JUnit 5相同assertThrows
。它还尊重现有的安全经理。
仍然存在一个问题:被测代码安装新的安全管理器时,它将完全替换测试设置的安全管理器。SecurityManager
我所知道的所有其他基于其他解决方案的问题都相同。
import java.security.Permission;
import static java.lang.System.getSecurityManager;
import static java.lang.System.setSecurityManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public enum ExitAssertions {
;
public static <E extends Throwable> void assertExits(final int expectedStatus, final ThrowingExecutable<E> executable) throws E {
final SecurityManager originalSecurityManager = getSecurityManager();
setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(final Permission perm) {
if (originalSecurityManager != null)
originalSecurityManager.checkPermission(perm);
}
@Override
public void checkPermission(final Permission perm, final Object context) {
if (originalSecurityManager != null)
originalSecurityManager.checkPermission(perm, context);
}
@Override
public void checkExit(final int status) {
super.checkExit(status);
throw new ExitException(status);
}
});
try {
executable.run();
fail("Expected System.exit(" + expectedStatus + ") to be called, but it wasn't called.");
} catch (final ExitException e) {
assertEquals(expectedStatus, e.status, "Wrong System.exit() status.");
} finally {
setSecurityManager(originalSecurityManager);
}
}
public interface ThrowingExecutable<E extends Throwable> {
void run() throws E;
}
private static class ExitException extends SecurityException {
final int status;
private ExitException(final int status) {
this.status = status;
}
}
}
您可以使用以下类:
@Test
void example() {
assertExits(0, () -> System.exit(0)); // succeeds
assertExits(1, () -> System.exit(1)); // succeeds
assertExits(2, () -> System.exit(1)); // fails
}
如有必要,可以轻松地将代码移植到JUnit 4,TestNG或任何其他框架。唯一特定于框架的元素无法通过测试。可以轻松地将其更改为与框架无关的内容(除Junit 4之外) Rule
有改进的空间,例如,assertExits()
可自定义消息的超载。