I am trying to implement an Annotation based Event System for my OpenGL Game Engine. I apply the #EventListener Annotation on the method which I want to be called like this:
#EventListener(type = Type.COLLISION)
public void OnCollision(CollisionEvent data)
{
System.out.println("HI");
}
The class in which this method sits implements an Empty Interface:
public class Sprite implements EventHandler
The EventDispatcher class:
public class EventDispatcher
{
private static List<EventHandler> registered = new ArrayList<EventHandler>();
public static void register(EventHandler EventHandler)
{
if (!registered.contains(EventHandler))
{
registered.add(EventHandler);
}
}
public static void unregister(EventHandler EventHandler)
{
if (registered.contains(EventHandler))
{
registered.remove(EventHandler);
}
}
public static List<EventHandler> getRegistered()
{
return registered;
}
public static void dispatch(final Event event)
{
new Thread()
{
#Override
public void run()
{
call(event);
}
}.start();
}
private static void call(final Event event)
{
for (EventHandler registered : getRegistered())
{
Method[] methods = registered.getClass().getMethods();
for (int i = 0; i < methods.length; i++)
{
System.out.println("Annotation Being Checked");
if (methods[i].isAnnotationPresent(EventListener.class))
{
System.out.println("Has Annotation");
Class<?>[] methodParams = methods[i].getParameterTypes();
if (methodParams.length < 1)
{
continue;
}
if (!event.getClass().getSimpleName().equals(methodParams[0].getSimpleName()))
{
continue;
}
try
{
methods[i].invoke(registered.getClass().newInstance(), event);
} catch (Exception exception)
{
System.err.println(exception);
}
} else System.out.println("No Annotation");
}
}
}
}
But when I run the program, It always prints out
Annotation Being Checked
No Annotation
multiple Times.
Can someone help? If more information is needed, please ask and I will edit the Question.
I setup a project based on your example and it's working fine. You will however see some "No Annotation" messages as your code evaluates all methods of the Sprite event handler. Even if you don't implement any additional methods other than OnCollision each class will inherit default methods from Object such as equals, hashCode or toString.
Test class:
public class SpriteTest {
public static void main(String[] args) {
EventDispatcher.register(new Sprite());
CollisionEvent collisionEvent = new CollisionEvent();
EventDispatcher.dispatch(collisionEvent);
}
}
Apart from that there are some obvious flaws in your code:
Don't use stateful static members (EventDispatcher.registered) unless you know what you're doing and are aware of the multithreading aspects that come with it
You store instances of EventHandler but only use the class information and create a new instance on the fly - why not register the class instead of an instance directly
You fork new Threads for each to be dispatched event. This is very bad practice as thread creation is a costly operation. Use a thread pool instead and submit runnables or callables
You check if the class' simple names match to see if a handler method is applicable. This will break when using inheritance and should be replaced by Class.isAssignableFrom
In general usage of annotations here is questionable. You're probably better off using dedicated interfaces for the different event types. Instead of a generic EventHandler there could be a CollisionEventHandler and so on...
Rough implementation idea
public interface CollisionEventHandler extends EventHandler {
void onCollision(CollisionEvent event);
}
public class Sprite implements CollisionEventHandler {
public void onCollision(CollisionEvent data) {
System.out.println("HI");
}
}
public class EventDispatcher {
...
static void call(final CollisionEvent event) {
getRegistered().stream()
.filter(handler -> handler instanceof CollisionEventHandler)
.map(handler -> (CollisionEventHandler) handler)
.forEach(handler -> handler.onCollision(event));
}
}
To handle different types of events you will need different call/dispatch methods. Maybe you can use the Visitor pattern (though I'm not a fan of it).
I have read this post: Is there a way in JMockit to call the original method from a mocked method?
but the recommend solution throws a NPE. Here is my source
static Map<String, Boolean> detectDeadlocks(int timeInSeconds) {
final Map<String, Boolean> deadlockMap = new LinkedHashMap<>();
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// I want to check if the method is run.
**deadlockMap.put("deadlock", isDeadlockAfterPeriod());**
}
}, timeInSeconds * 1000);
return deadlockMap;
}
I want to be able to invoke isDeadlockAfterPeriod in my unit test. This is a static method that I have mocked in my unit tests.
My unit test code
#Test
public void testDetectDeadlocks() throws Exception {
new Expectations(){
{
// Called from inside the TimerTask.
ClassUnderTest.isDeadlockAfterPeriod();
result = false;
}
};
TimerMockUp tmu = new TimerMockUp();
Deadlocks.detectDeadlocks(0);
Assert.assertEquals(1, tmu.scheduleCount);
}
class TimerMockUp extends MockUp<Timer> {
int scheduleCount = 0;
#Mock
public void $init() {}
#Mock
public void schedule(Invocation invocation, TimerTask task, long delay) {
scheduleCount ++;
invocation.proceed(); // Trying to call the real method, but this throws a NPE.
}
}
Error stack trace is seen with JUnit in Eclipse.
java.lang.NullPointerException
at com.myproject.test.DeadlocksTest$TimerMockUp.schedule(DeadlocksTest.java:78)
at com.myproject.test.Deadlocks.detectDeadlocks(Deadlocks.java:41)
at com.myproject.test.DeadlocksTest.testDetectDeadlocks(DeadlocksTest.java:86)
Your problem is that you are also faking the Timer's constructor (and not only the schedule method).
By doing so, you are preventing the correct initialization of the Timer, and as you are then using its real implementation, it fails to do so.
Specifically (with the sources I have), you are preventing the initialization of its queue field, which is used on its mainLoop() method (the one that will call to your TimerTask.run()).
Also, you need to do partial mocking of Deadlocks class, as I understand that isDeadlockAfterPeriod is also a static method for the said class.
I'll leave you here a working example:
Deadlocks.class
public class Deadlocks {
public static Map<String, Boolean> detectDeadlocks(int timeInSeconds) {
final Map<String, Boolean> deadlockMap = new LinkedHashMap<>();
new Timer()// this will be the real constructor
.schedule( // this will be first mocked, then really executed
new TimerTask() {
#Override
public void run() {
deadlockMap.put("deadlock", isDeadlockAfterPeriod()); // this will put false after the mock is called
}
}, timeInSeconds * 1000);
return deadlockMap;
}
public static Boolean isDeadlockAfterPeriod() {
return true; // this, we will mock it
}
}
Test class
#RunWith(JMockit.class)
public TestClass{
#Test
public void testDetectDeadlocks() throws Exception {
new Expectations(Deadlocks.class){ // do partial mocking of the Deadlock class
{
// Called from inside the TimerTask.
Deadlocks.isDeadlockAfterPeriod();
result = false;
}
};
// prepare the fake
TimerMockUp tmu = new TimerMockUp();
// execute the code
Map<String, Boolean> result = Deadlocks.detectDeadlocks(0);
// assert results
assertThat(tmu.scheduleCount, is(1));
assertThat(result.size(), is(1));
assertThat(result.get("deadlock"), is(false));
}
class TimerMockUp extends MockUp<Timer> {
int scheduleCount = 0;
#Mock
public void schedule(Invocation invocation, TimerTask task, long delay) {
scheduleCount ++;
invocation.proceed();
}
}
}
In general, be very careful when faking constructors, as you may leave the instances in an inconsistent state.
I have following method for test:
public class classToTest{
#Autowired
private Alternator alternator;
public void methodToTest(){
Thread t = new Thread(new Runnable() {
public void run() {
while(true) {
if(alternator.get()) {
System.out.print("Hello");
alternator.set(false);
}
}
}
};
t.start()
}
}
I need to check that was invoked method
alternator.set(false);
How can I do it?
Instead of starting a thread directly, can you pass in an "Executor" instance?
For example...
public class ClassToTest{
#Autowired
private Alternator alternator;
#Autowired #Qualifier("myExecutor")
private java.util.concurrent.Executor executor;
public void methodToTest() {
Runnable runnable = new Runnable() {
public void run() {
while(true) {
if(alternator.get()) {
System.out.print("Hello");
alternator.set(false);
}
}
};
executor.execute(runnable);
}
}
Now you can test this easier...
public class ClassToTestTest {
...
#Before
public void setup() {
alternator = mock(Alternator.class);
executor = mock(Executor.class);
obj = new ClassToTest();
ReflectionTestUtils.setField(obj, "alternator", alternator);
ReflectionTestUtils.setField(obj, "executor", executor);
}
#Test
public void shouldStartRunnable() {
obj.methodToTest();
ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(executor).execute(runnableCaptor.capture());
Runnable runnable = runnableCaptor.getValue();
// Now test your actual "runnable"...
when(alternator.get()).thenReturn(true);
runnable.run();
verify(alternator).set(false);
}
}
(Have not tried to compile this, so I apologise if there are any mistakes!!)
Though Bret's post of passing in an executor is very much recommended, you can use the timeout() mock verification setting to test for asynchronous conditions.
verify(alternator, timeout(500)).set(false);
Note that this will necessarily increase the flakiness of your test (i.e. the likelihood that the test fails when the code passes). With a sensible timeout value, that flakiness should be negligible, but if you're making this a part of your core test infrastructure you may consider refactoring to allow for synchronous execution in the test.
I have an application built upon Spring Boot. There is simple controller with a method which creates new Thread and starts it. However a runnable executes unix command (nc) (used ProcessBuilder for that). Thus when I'm runnning it on the windows I get exceptions from started thread. Indeed it can not run unix program. Now I would like to write a test for this controller, but I'm wondering is it possible and reasonable. I was thinking about changing behaviour of runnable task just for testing, although I don't know how can it be done. Thanks for any help and other ideas/solutions for this case.
Controller:
#Controller
public class TaskController {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(task-%d").build();
#RequestMapping(value = "/startTask")
public #ResponseBody ResponseEntity<String> startTask() {
Runnable runnable= new Task();
threadFactory.newThread(runnable).start();
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
Task:
public class Task implements Runnable {
#Override
public void run() {
// start unix process
}
}
Application class:
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Integration Test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest("server.port=0")
#DirtiesContext
public class ApplicationTest {
#Value("${local.server.port}")
private int port;
#Test
public void shouldStartTask() throws Exception {
// when
ResponseEntity<String> entity = new TestRestTemplate().getForEntity("http://localhost:" + this.port + "/startTask", String.class);
// then
assertThat(entity.getStatusCode()).isSameAs(HttpStatus.ACCEPTED);
}
}
You might find it easier to test your program if you the extract the processing logic of your application (which does things using threads) from your controller logic, placing the processing logic in a separate service layer, which your controller delegates to. Design the service layer to have an API that is easy to unit test, by providing methods for accessing its current state, not just for performing actions. Use dependency injection to connect your controller to your service layer.
So, something like this:
public interface Service
{
// Sets this.wasTaskStarted() == true
void startTask();
boolean wasTaskStarted();
void awaitCompletionOfTask();
}
#Controller
public class TaskController {
private final Service service;
#Autowired
public TaskController(Service service) {
this.service = service;
}
#RequestMapping(value = "/startTask")
public #ResponseBody ResponseEntity<String> startTask() {
service.startTask();
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
public ServiceImpl implements Service {
private final ThreadFactor threadFactory = new ....;
private Thread taskTread;
#Override
public synchronized void startTask() {
if (taskTread == null) {
taskTread = threadFactory.newThread(new Task());
taskTread.start();
notifyAll();
}
// else already started
}
#Override
public synchronized boolean wasTaskStarted() {
return taskTread != null;
}
#Override
public synchronized void awaitCompletionOfTask() {
while (taskTread == null) {
wait();
}
taskTread.join();
}
}
To test that your controller starts a task, you just need to test that Service.wasTaskStarted() is true after calling TaskController.startTask().
You also have to test your service layer:
public class ServiceImplTest
{
#Test
public void testStartTask() {
final ServiceImpl service = new ServiceImpl(....);
service.startTask();
assert(service.wasTastStarted());
}
#Test
public void testRunTask() {
final ServiceImpl service = new ServiceImpl(....);
service.startTask();
service.awaitCompletionOfTask();
// Add assertions here to test that the task did what it ought to do
}
}
Thanks for the suggestion. You just opened my mind and I changed the design a bit. I resigned from an integration test. From business point of view, I don't need to check whether task has been started or even completed. Now it looks as follows:
Controller:
#Controller
public class TaskController {
private ThreadService threadService;
#Autowired
public TaskController (ThreadService threadService) {
this.threadService= threadService;
}
#RequestMapping(value = "/startTask")
public #ResponseBody ResponseEntity<String> startTask() {
// some conditions here which I would like to test
threadService.startNewThread(new Task());
return new ResponseEntity<String>("Task started", HttpStatus.ACCEPTED);
}
}
Task:
public class Task implements Runnable {
#Override
public void run() {
// start unix process
}
}
Thread service:
#Component
public class ThreadService {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("task-%d").build();
public void startNewThread(Runnnable task) {
threadFactory.newThread(task).start();
}
}
And I decided to unit test my controller, stubbing ThreadService with mockito:
#RunWith(MockitoJUnitRunner.class)
public class TaskControllerTest {
#Mock
ThreadService threadService;
#InjectMocks
private TaskController objectUnderTest;
#Test
public void shouldStartTask() throws FileNotFoundException {
// when
ResponseEntity<String> response = objectUnderTest.startTask();
// then
assertThat(response.getStatusCode()).isSameAs(HttpStatus.ACCEPTED);
// more assertions
}
I've got a few methods that should call System.exit() on certain inputs. Unfortunately, testing these cases causes JUnit to terminate! Putting the method calls in a new Thread doesn't seem to help, since System.exit() terminates the JVM, not just the current thread. Are there any common patterns for dealing with this? For example, can I subsitute a stub for System.exit()?
[EDIT] The class in question is actually a command-line tool which I'm attempting to test inside JUnit. Maybe JUnit is simply not the right tool for the job? Suggestions for complementary regression testing tools are welcome (preferably something that integrates well with JUnit and EclEmma).
Indeed, Derkeiler.com suggests:
Why System.exit() ?
Instead of terminating with System.exit(whateverValue), why not throw an unchecked exception? In normal use it will drift all the way out to the JVM's last-ditch catcher and shut your script down (unless you decide to catch it somewhere along the way, which might be useful someday).
In the JUnit scenario it will be caught by the JUnit framework, which will report that
such-and-such test failed and move smoothly along to the next.
Prevent System.exit() to actually exit the JVM:
Try modifying the TestCase to run with a security manager that prevents calling System.exit, then catch the SecurityException.
public class NoExitTestCase extends TestCase
{
protected static class ExitException extends SecurityException
{
public final int status;
public ExitException(int status)
{
super("There is no escape!");
this.status = status;
}
}
private static class NoExitSecurityManager extends SecurityManager
{
#Override
public void checkPermission(Permission perm)
{
// allow anything.
}
#Override
public void checkPermission(Permission perm, Object context)
{
// allow anything.
}
#Override
public void checkExit(int status)
{
super.checkExit(status);
throw new ExitException(status);
}
}
#Override
protected void setUp() throws Exception
{
super.setUp();
System.setSecurityManager(new NoExitSecurityManager());
}
#Override
protected void tearDown() throws Exception
{
System.setSecurityManager(null); // or save and restore original
super.tearDown();
}
public void testNoExit() throws Exception
{
System.out.println("Printing works");
}
public void testExit() throws Exception
{
try
{
System.exit(42);
} catch (ExitException e)
{
assertEquals("Exit status", 42, e.status);
}
}
}
Update December 2012:
Will proposes in the comments using System Rules, a collection of JUnit(4.9+) rules for testing code which uses java.lang.System.
This was initially mentioned by Stefan Birkner in his answer in December 2011.
System.exit(…)
Use the ExpectedSystemExit rule to verify that System.exit(…) is called.
You could verify the exit status, too.
For instance:
public void MyTest {
#Rule
public final ExpectedSystemExit exit = ExpectedSystemExit.none();
#Test
public void noSystemExit() {
//passes
}
#Test
public void systemExitWithArbitraryStatusCode() {
exit.expectSystemExit();
System.exit(0);
}
#Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
System.exit(0);
}
}
The library System Lambda has a method catchSystemExit.With this rule you are able to test code, that calls System.exit(...):
public class MyTest {
#Test
public void systemExitWithArbitraryStatusCode() {
SystemLambda.catchSystemExit(() -> {
//the code under test, which calls System.exit(...);
});
}
#Test
public void systemExitWithSelectedStatusCode0() {
int status = SystemLambda.catchSystemExit(() -> {
//the code under test, which calls System.exit(0);
});
assertEquals(0, status);
}
}
For Java 5 to 7 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(...);
}
#Test
public void systemExitWithSelectedStatusCode0() {
exit.expectSystemExitWithStatus(0);
//the code under test, which calls System.exit(0);
}
}
Full disclosure: I'm the author of both libraries.
How about injecting an "ExitManager" into this Methods:
public interface ExitManager {
void exit(int exitCode);
}
public class ExitManagerImpl implements ExitManager {
public void exit(int exitCode) {
System.exit(exitCode);
}
}
public class ExitManagerMock implements ExitManager {
public bool exitWasCalled;
public int exitCode;
public void exit(int exitCode) {
exitWasCalled = true;
this.exitCode = exitCode;
}
}
public class MethodsCallExit {
public void CallsExit(ExitManager exitManager) {
// whatever
if (foo) {
exitManager.exit(42);
}
// whatever
}
}
The production code uses the ExitManagerImpl and the test code uses ExitManagerMock and can check if exit() was called and with which exit code.
You actually can mock or stub out the System.exit method, in a JUnit test.
For example, using JMockit you could write (there are other ways as well):
#Test
public void mockSystemExit(#Mocked("exit") System mockSystem)
{
// Called by code under test:
System.exit(); // will not exit the program
}
EDIT: Alternative test (using latest JMockit API) which does not allow any code to run after a call to System.exit(n):
#Test(expected = EOFException.class)
public void checkingForSystemExitWhileNotAllowingCodeToContinueToRun() {
new Expectations(System.class) {{ System.exit(anyInt); result = new EOFException(); }};
// From the code under test:
System.exit(1);
System.out.println("This will never run (and not exit either)");
}
One trick we used in our code base was to have the call to System.exit() be encapsulated in a Runnable impl, which the method in question used by default. To unit test, we set a different mock Runnable. Something like this:
private static final Runnable DEFAULT_ACTION = new Runnable(){
public void run(){
System.exit(0);
}
};
public void foo(){
this.foo(DEFAULT_ACTION);
}
/* package-visible only for unit testing */
void foo(Runnable action){
// ...some stuff...
action.run();
}
...and the JUnit test method...
public void testFoo(){
final AtomicBoolean actionWasCalled = new AtomicBoolean(false);
fooObject.foo(new Runnable(){
public void run(){
actionWasCalled.set(true);
}
});
assertTrue(actionWasCalled.get());
}
I like some of the answers already given but I wanted to demonstrate a different technique that is often useful when getting legacy code under test. Given code like:
public class Foo {
public void bar(int i) {
if (i < 0) {
System.exit(i);
}
}
}
You can do a safe refactoring to create a method that wraps the System.exit call:
public class Foo {
public void bar(int i) {
if (i < 0) {
exit(i);
}
}
void exit(int i) {
System.exit(i);
}
}
Then you can create a fake for your test that overrides exit:
public class TestFoo extends TestCase {
public void testShouldExitWithNegativeNumbers() {
TestFoo foo = new TestFoo();
foo.bar(-1);
assertTrue(foo.exitCalled);
assertEquals(-1, foo.exitValue);
}
private class TestFoo extends Foo {
boolean exitCalled;
int exitValue;
void exit(int i) {
exitCalled = true;
exitValue = i;
}
}
This is a generic technique for substituting behavior for test cases, and I use it all the time when refactoring legacy code. It not usually where I'm going to leave thing, but an intermediate step to get the existing code under test.
For VonC's answer to run on JUnit 4, I've modified the code as follows
protected static class ExitException extends SecurityException {
private static final long serialVersionUID = -1982617086752946683L;
public final int status;
public ExitException(int status) {
super("There is no escape!");
this.status = status;
}
}
private static class NoExitSecurityManager extends SecurityManager {
#Override
public void checkPermission(Permission perm) {
// allow anything.
}
#Override
public void checkPermission(Permission perm, Object context) {
// allow anything.
}
#Override
public void checkExit(int status) {
super.checkExit(status);
throw new ExitException(status);
}
}
private SecurityManager securityManager;
#Before
public void setUp() {
securityManager = System.getSecurityManager();
System.setSecurityManager(new NoExitSecurityManager());
}
#After
public void tearDown() {
System.setSecurityManager(securityManager);
}
Create a mock-able class that wraps System.exit()
I agree with EricSchaefer. But if you use a good mocking framework like Mockito a simple concrete class is enough, no need for an interface and two implementations.
Stopping test execution on System.exit()
Problem:
// do thing1
if(someCondition) {
System.exit(1);
}
// do thing2
System.exit(0)
A mocked Sytem.exit() will not terminate execution. This is bad if you want to test that thing2 is not executed.
Solution:
You should refactor this code as suggested by martin:
// do thing1
if(someCondition) {
return 1;
}
// do thing2
return 0;
And do System.exit(status) in the calling function. This forces you to have all your System.exit()s in one place in or near main(). This is cleaner than calling System.exit() deep inside your logic.
Code
Wrapper:
public class SystemExit {
public void exit(int status) {
System.exit(status);
}
}
Main:
public class Main {
private final SystemExit systemExit;
Main(SystemExit systemExit) {
this.systemExit = systemExit;
}
public static void main(String[] args) {
SystemExit aSystemExit = new SystemExit();
Main main = new Main(aSystemExit);
main.executeAndExit(args);
}
void executeAndExit(String[] args) {
int status = execute(args);
systemExit.exit(status);
}
private int execute(String[] args) {
System.out.println("First argument:");
if (args.length == 0) {
return 1;
}
System.out.println(args[0]);
return 0;
}
}
Test:
public class MainTest {
private Main main;
private SystemExit systemExit;
#Before
public void setUp() {
systemExit = mock(SystemExit.class);
main = new Main(systemExit);
}
#Test
public void executeCallsSystemExit() {
String[] emptyArgs = {};
// test
main.executeAndExit(emptyArgs);
verify(systemExit).exit(1);
}
}
System Stubs - https://github.com/webcompere/system-stubs - is also able to solve this problem. It shares System Lambda's syntax for wrapping around code that we know will execute System.exit, but that can lead to odd effects when other code unexpectedly exits.
Via the JUnit 5 plugin, we can provide insurance that any exit will be converted to an exception:
#ExtendWith(SystemStubsExtension.class)
class SystemExitUseCase {
// the presence of this in the test means System.exit becomes an exception
#SystemStub
private SystemExit systemExit;
#Test
void doSomethingThatAccidentallyCallsSystemExit() {
// this test would have stopped the JVM, now it ends in `AbortExecutionException`
// System.exit(1);
}
#Test
void canCatchSystemExit() {
assertThatThrownBy(() -> System.exit(1))
.isInstanceOf(AbortExecutionException.class);
assertThat(systemExit.getExitCode()).isEqualTo(1);
}
}
Alternatively, the assertion-like static method can also be used:
assertThat(catchSystemExit(() -> {
//the code under test
System.exit(123);
})).isEqualTo(123);
A quick look at the api, shows that System.exit can throw an exception esp. if a securitymanager forbids the shutdown of the vm. Maybe a solution would be to install such a manager.
You can use the java SecurityManager to prevent the current thread from shutting down the Java VM. The following code should do what you want:
SecurityManager securityManager = new SecurityManager() {
public void checkPermission(Permission permission) {
if ("exitVM".equals(permission.getName())) {
throw new SecurityException("System.exit attempted and blocked.");
}
}
};
System.setSecurityManager(securityManager);
You can test System.exit(..) with replacing Runtime instance.
E.g. with TestNG + Mockito:
public class ConsoleTest {
/** Original runtime. */
private Runtime originalRuntime;
/** Mocked runtime. */
private Runtime spyRuntime;
#BeforeMethod
public void setUp() {
originalRuntime = Runtime.getRuntime();
spyRuntime = spy(originalRuntime);
// Replace original runtime with a spy (via reflection).
Utils.setField(Runtime.class, "currentRuntime", spyRuntime);
}
#AfterMethod
public void tearDown() {
// Recover original runtime.
Utils.setField(Runtime.class, "currentRuntime", originalRuntime);
}
#Test
public void testSystemExit() {
// Or anything you want as an answer.
doNothing().when(spyRuntime).exit(anyInt());
System.exit(1);
verify(spyRuntime).exit(1);
}
}
There are environments where the returned exit code is used by the calling program (such as ERRORLEVEL in MS Batch). We have tests around the main methods that do this in our code, and our approach has been to use a similar SecurityManager override as used in other tests here.
Last night I put together a small JAR using Junit #Rule annotations to hide the security manager code, as well as add expectations based on the expected return code. http://code.google.com/p/junitsystemrules/
Most solutions will
terminate the test (method, not the entire run) the moment System.exit() is called
ignore an already installed SecurityManager
Sometimes be quite specific to a test framework
restrict to be used at max once per test case
Thus, most solutions are not suited for situations where:
Verification of side-effects are to be performed after the call to System.exit()
An existing security manager is part of the testing.
A different test framework is used.
You want to have multiple verifications in a single test case. This may be strictly not recommended, but can be very convenient at times, especially in combination with assertAll(), for example.
I was not happy with the restrictions imposed by the existing solutions presented in the other answers, and thus came up with something on my own.
The following class provides a method assertExits(int expectedStatus, Executable executable) which asserts that System.exit() is called with a specified status value, and the test can continue after it. It works the same way as JUnit 5 assertThrows. It also respects an existing security manager.
There is one remaining problem: When the code under test installs a new security manager which completely replaces the security manager set by the test. All other SecurityManager-based solutions known to me suffer the same problem.
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;
}
}
}
You can use the class like this:
#Test
void example() {
assertExits(0, () -> System.exit(0)); // succeeds
assertExits(1, () -> System.exit(1)); // succeeds
assertExits(2, () -> System.exit(1)); // fails
}
The code can easily be ported to JUnit 4, TestNG, or any other framework, if necessary. The only framework-specific element is failing the test. This can easily be changed to something framework-independent (other than a Junit 4 Rule
There is room for improvement, for example, overloading assertExits() with customizable messages.
Use Runtime.exec(String command) to start JVM in a separate process.
There is a minor problem with the SecurityManager solution. Some methods, such as JFrame.exitOnClose, also call SecurityManager.checkExit. In my application, I didn't want that call to fail, so I used
Class[] stack = getClassContext();
if (stack[1] != JFrame.class && !okToExit) throw new ExitException();
super.checkExit(status);
A generally useful approach that can be used for unit and integration testing, is to have a package private (default access) mockable runner class that provides run() and exit() methods. These methods can be overridden by Mock or Fake test classes in the test modules.
The test class (JUnit or other) provides exceptions that the exit() method can throw in place of System.exit().
package mainmocked;
class MainRunner {
void run(final String[] args) {
new MainMocked().run(args);
}
void exit(final int status) {
System.exit(status);
}
}
the class with main() below, also has an altMain() to receive the mock or fake runner, when unit or integration testing:
package mainmocked;
public class MainMocked {
private static MainRunner runner = new MainRunner();
static void altMain(final String[] args, final MainRunner inRunner) {
runner = inRunner;
main(args);
}
public static void main(String[] args) {
try {
runner.run(args);
} catch (Throwable ex) {
// Log("error: ", ex);
runner.exit(1);
}
runner.exit(0);
} // main
public void run(String[] args) {
// do things ...
}
} // class
A simple mock (with Mockito) would be:
#Test
public void testAltMain() {
String[] args0 = {};
MainRunner mockRunner = mock(MainRunner.class);
MainMocked.altMain(args0, mockRunner);
verify(mockRunner).run(args0);
verify(mockRunner).exit(0);
}
A more complex test class would use a Fake, in which run() could do anything, and an Exception class to replace System.exit():
private class FakeRunnerRuns extends MainRunner {
#Override
void run(String[] args){
new MainMocked().run(args);
}
#Override
void exit(final int status) {
if (status == 0) {
throw new MyMockExitExceptionOK("exit(0) success");
}
else {
throw new MyMockExitExceptionFail("Unexpected Exception");
} // ok
} // exit
} // class
Another technique here is to introduce additional code into the (hopefully small number of) places where the logic does the System.exit(). This additional code then avoids doing the System.exit() when the logic is being called as part of unit test. For example, define a package private constant like TEST_MODE which is normally false. Then have the unit test code set this true and add logic to the code under test to check for that case and bypass the System.exit call and instead throw an exception that the unit test logic can catch. By the way, in 2021 and using something like spotbugs, this problem can manifest itself in the obscure error that "java.io.IOException: An existing connection was forcibly closed by the remote host".
Calling System.exit() is a bad practice, unless it's done inside a main(). These methods should be throwing an exception which, ultimately, is caught by your main(), who then calls System.exit with the appropriate code.