Hầu hết các giải pháp sẽ
- chấm dứt thử nghiệm (phương thức, không phải toàn bộ hoạt động) thời điểm
System.exit()
được gọi
- bỏ qua một cài đặt đã
SecurityManager
- Đôi khi khá cụ thể cho một khung kiểm tra
- hạn chế sử dụng tối đa một lần cho mỗi trường hợp thử nghiệm
Vì vậy, hầu hết các giải pháp không phù hợp cho các tình huống:
- Việc xác minh tác dụng phụ sẽ được thực hiện sau cuộc gọi đến
System.exit()
- Một người quản lý bảo mật hiện có là một phần của thử nghiệm.
- Một khung kiểm tra khác nhau được sử dụng.
- Bạn muốn có nhiều xác minh trong một trường hợp thử nghiệm. Điều này có thể không được khuyến khích, nhưng đôi khi có thể rất thuận tiện, đặc biệt là kết hợp với
assertAll()
, ví dụ.
Tôi không hài lòng với các hạn chế được áp đặt bởi các giải pháp hiện có trong các câu trả lời khác, và do đó tự mình nghĩ ra một cái gì đó.
Lớp sau đây cung cấp một phương thức assertExits(int expectedStatus, Executable executable)
xác nhận System.exit()
được gọi với một status
giá trị được chỉ định và kiểm tra có thể tiếp tục sau nó. Nó hoạt động tương tự như JUnit 5 assertThrows
. Nó cũng tôn trọng một người quản lý an ninh hiện có.
Có một vấn đề còn lại: Khi mã được kiểm tra cài đặt trình quản lý bảo mật mới thay thế hoàn toàn trình quản lý bảo mật do thử nghiệm đặt. Tất cả các SecurityManager
giải pháp dựa trên cơ sở khác mà tôi biết đều gặp phải vấn đề tương tự.
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;
}
}
}
Bạn có thể sử dụng lớp như thế này:
@Test
void example() {
assertExits(0, () -> System.exit(0)); // succeeds
assertExits(1, () -> System.exit(1)); // succeeds
assertExits(2, () -> System.exit(1)); // fails
}
Mã có thể dễ dàng được chuyển đến JUnit 4, TestNG hoặc bất kỳ khung nào khác, nếu cần. Yếu tố khung cụ thể duy nhất là thất bại trong bài kiểm tra. Điều này có thể dễ dàng được thay đổi thành một cái gì đó độc lập với khung (khác với Junit 4 Rule
Có chỗ để cải thiện, ví dụ, quá tải assertExits()
với các thông điệp có thể tùy chỉnh.