Reputation: 2388
I am implementing some tests for an existing Java Swing application, so that I can safely refactor and extend the code without breaking anything. I started with some unit tests in JUnit, since that seems the simplest way to get started, but now my priority is to create some end-to-end tests to exercise the application as a whole.
I am starting the application afresh in each test by putting each test method in a separate test case, and using the fork="yes"
option in Ant's junit
task. However, some of the use cases I would like to implement as tests involve the user exiting the application, which results in one of the methods calling System.exit(0). This is regarded by JUnit as an error: junit.framework.AssertionFailedError: Forked Java VM exited abnormally
.
Is there a way to tell JUnit that exiting with a return code of zero is actually OK?
Upvotes: 13
Views: 22384
Reputation: 24520
The library System Rules has a JUnit rule called ExpectedSystemExit. With this rule you are able to test code, that calls 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)
}
}
System Rules needs at least JUnit 4.9.
Full disclosure: I'm the author of System Rules.
Upvotes: 33
Reputation: 31700
If anybody needs this functionality for JUnit 5, I've written an extension to do this. This is a simple annotation you can use to tell your test case to expect and exit status code or a specific exit status code.
For example, any exit code will do:
public class MyTestCases {
@Test
@ExpectSystemExit
public void thatSystemExitIsCalled() {
System.exit(1);
}
}
If we want to look for a specific code:
public class MyTestCases {
@Test
@ExpectSystemExitWithStatus(1)
public void thatSystemExitIsCalled() {
System.exit(1);
}
}
Upvotes: 5
Reputation: 61526
How I deal with that is to install a security manager that throws an exception when System.exit is called. Then there is code that catches the exception and doesn't fail the test.
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)
{
}
}
And then in the code, something like:
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);
Upvotes: 8
Reputation: 1500515
Could you abstract out the "system exiting" into a new dependency, so that in your tests you could just have a fake which records the fact that exit has been called (and the value), but use an implementation which calls System.exit
in the real application?
Upvotes: 6