0

I want to unit test Java code that calls System.exit(-1) and want it to just do nothing instead of exiting the process. The underlying reason is that otherwise JaCoCo does not work properly and project guidelines want to see that line covered. Changing the tested code is not an option, too. Other calls to System should work normally. PowerMockito 2.0.7 is already used in the project and should be used here, too. My current Java version is 1.8.0_181 on Windows.

I tried with

PowerMockito.spy(System.class);
PowerMockito.doNothing().when(System.class, "exit", ArgumentMatchers.any(int.class));
//here comes the code under test that calls System.exit

It does not seem to work, System.exit seems to exit the process anyway. How do it get this to work?

Tobias B.
  • 107
  • 1
  • 8
  • If you are using JUnit 4.9 <= version < 5, there seems to be [no need for PowerMockito](https://stackoverflow.com/questions/309396/java-how-to-test-methods-that-call-system-exit/3096741). – Turing85 Aug 01 '20 at 10:47
  • Thanks for pointing out this question. I had already seen it before and would prefer a more concise solution with PowerMockito, but if that's not possible I'll discuss with the project maintainers whether using the SecurityManager approach is also OK. Another related question is https://stackoverflow.com/questions/54055881/cant-mock-java-lang-systemexitint-method-with-powermock/55106568#55106568, but is has no solution with PowerMockito, too. – Tobias B. Aug 01 '20 at 11:51
  • One should always consider that PowerMockito uses byte code manipulation and thus may cause issues when using JaCoCo (which also uses byte code manipulation to add counters for coverage reports)... so yeah. Not opting for PowerMockito is probably a good choice. – Turing85 Aug 01 '20 at 11:54
  • I now tested the alternative solution with a custom SecurityManager. Sadly, JaCoCo does not treat the System.exit line as covered when it throws an Exception. Therefore, this does not solve the problem. – Tobias B. Aug 01 '20 at 12:16

1 Answers1

1

I think you should replace both the lines in your sample code

PowerMockito.spy(System.class);
PowerMockito.doNothing.....

to

PowerMockito.mockStatic(System.class);

This change works in my local as System.exit does nothing because of the mock on static method.

Also, I hope you are using PrepareForTest annotation

@PrepareForTest(CLASS_UNDER_TEST)

The spy method is to call real methods and have some wrapper around the non-static methods. Since you need a mock for static methods, mockStatic method should be used instead.

Update 1

The PowerMockito mockStatic method by default creates mock for all the static methods within the class. I don't have any clean solution. But, I can suggest a solution which looks ugly but does what is needed i.e only mock specific static method and remaining methods are invoking real methods. PoweMockito's mockStatic method is internally calling DefaultMockCreator to mock the static methods.

@RunWith(PowerMockRunner.class)
public class StaticTest {

  @Test
  public void testMethod() throws Exception {

    // Get static methods for which mock is needed
    Method exitMethod = System.class.getMethod("exit", int.class);
    Method[] methodsToMock = new Method[] {exitMethod};

    // Create mock for only those static methods
    DefaultMockCreator.mock(System.class, true, false, null, null, methodsToMock);

    System.exit(-1); // This will be mocked
    System.out.println(System.currentTimeMillis()); // This will call up real methods
  }
}

As per the PowerMockito documentation, the right way to call static void method is -

PowerMockito.mockStatic(SomeClass.class);
PowerMockito.doNothing().when(SomeClass.class);
SomeClass.someVoidMethod();

Reference - https://github.com/powermock/powermock/wiki/Mockito#how-to-stub-void-static-method-to-throw-exception

This should create the mock behaviour for the specific static void method. Unfortunately, this doesn't work for System Class because System class is final. Had it been not final, this would have worked. I tried it and I got this exception -

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class java.lang.System
Mockito cannot mock/spy because :
 - final class

Code -

@Test
public void testMethod() throws Exception {
    PowerMockito.mockStatic(System.class);
    PowerMockito.doNothing().when(System.class);
    System.exit(-1); // mockito error coming here

    System.exit(-1);
    System.currentTimeMillis();
}
SKumar
  • 1,940
  • 1
  • 7
  • 12