Sebagian besar solusi akan melakukannya
- hentikan tes (metode, bukan seluruh proses) saat
System.exit()
ini disebut
- abaikan yang sudah diinstal
SecurityManager
- Terkadang cukup spesifik untuk kerangka uji
- batasi untuk digunakan maksimal satu kali per test case
Dengan demikian, sebagian besar solusi tidak cocok untuk situasi di mana:
- Verifikasi efek samping harus dilakukan setelah panggilan ke
System.exit()
- Manajer keamanan yang ada adalah bagian dari pengujian.
- Kerangka uji yang berbeda digunakan.
- Anda ingin memiliki beberapa verifikasi dalam satu test case. Ini mungkin sangat tidak dianjurkan, tetapi bisa sangat nyaman di kali, terutama dalam kombinasi dengan
assertAll()
, misalnya.
Saya tidak senang dengan pembatasan yang diberlakukan oleh solusi yang ada yang disajikan dalam jawaban lain, dan dengan demikian muncul dengan sesuatu sendiri.
Kelas berikut menyediakan metode assertExits(int expectedStatus, Executable executable)
yang menyatakan bahwa System.exit()
dipanggil dengan status
nilai yang ditentukan , dan tes dapat dilanjutkan setelahnya. Ini bekerja dengan cara yang sama seperti JUnit 5assertThrows
. Itu juga menghormati manajer keamanan yang ada.
Ada satu masalah yang tersisa: Ketika kode yang diuji menginstal manajer keamanan baru yang sepenuhnya menggantikan manajer keamanan yang ditetapkan oleh tes. Semua SecurityManager
solusi berbasis lainnya yang saya kenal menderita masalah yang sama.
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;
}
}
}
Anda dapat menggunakan kelas seperti ini:
@Test
void example() {
assertExits(0, () -> System.exit(0)); // succeeds
assertExits(1, () -> System.exit(1)); // succeeds
assertExits(2, () -> System.exit(1)); // fails
}
Kode dapat dengan mudah porting ke JUnit 4, TestNG, atau kerangka kerja lainnya, jika perlu. Satu-satunya elemen kerangka spesifik gagal tes. Ini dapat dengan mudah diubah menjadi sesuatu yang independen kerangka kerja (selain Junit 4 Rule
Ada ruang untuk perbaikan, misalnya, kelebihan muatan assertExits()
dengan pesan yang dapat disesuaikan.