Explain Codes LogoExplain Codes Logo

How to test methods that call System.exit()?

java
test-engineering
java-8
best-practices
Nikita BarsukovbyNikita Barsukov·Oct 27, 2024
TLDR
// With the power of JUnit's ExpectedSystemExit rule, we can trap the exit like a ghostbuster! import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ExpectedSystemExit; public class CodeGhostbuster { @Rule public final ExpectedSystemExit exit = ExpectedSystemExit.none(); @Test public void doesItReallyExit() { exit.expectSystemExit(); System.exit(42); // Ah, the answer to life, universe, everything! } }

Trap the exit code and prevent pretty much "murder" of JVM during your tests!

Exit alternatives

If using ExpectedSystemExit feels wrong like eating pineapple on pizza, here are a few alternatives:

The aggressive pacifist: Unchecked exceptions

public void terminateWithGrace(int status) { throw new RuntimeException("Roses are red, my love for testing is endless, exiting with status " + status); }

No JVM termination, just hurt feelings through exception handling.

The protective parent: Custom SecurityManager

public class OverprotectedSecurityManager extends SecurityManager { @Override public void checkExit(int status) { super.checkExit(status); throw new SecurityException("Young man, where do you think you're going?!"); } }

Treats termination like it's past curfew time! SecurityException: Caught red-handed!

The lamb whisperer: System Lambda

import static com.github.stefanbirkner.systemlambda.SystemLambda.catchSystemExit; public class CodeWhisperer { @Test public void exitsLikeANinja() { int status = catchSystemExit(() -> { System.exit(0); // Code ninja, vanishing! }); assertEquals(0, status); // No trace left behind. } }

System Lambda library, for the testing purists who love their Java 8+.

Advanced strategies

Surely the above techniques might've satisfied your "exit-ecution" problems. However, if you had a dose of problem steroids, let's dive deeper.

Java 21+: New sheriff in town

At the dawn of Java 21, a new law -Djava.security.manager=allow was put in order. Ensure that's set right!

Global state changes: Fixing the butterfly effect

If you sinned and mutated global state (a static variable perhaps), confess and reset in your pre-test and post-test conditions.

Coverage tools: The spy who loved me

Tools like EclEmma for test coverage need to survive the execution for accurate reporting. Ensure compatibility.

Continuous integration: The grand stage

In staged CI pipelines, System.exit() can unexpected pull the curtains! Use the strategies above to let the show go on.