如何使用PowerMockito模拟System.exit?

huangapple go评论49阅读模式
英文:

How to mock System.exit with PowerMockito?

问题

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?

英文:

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?

答案1

得分: 1

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

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.

```java
@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 - PowerMockito Mockito Documentation

This should create the mock behavior 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();
}
英文:

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();
}

huangapple
  • 本文由 发表于 2020年8月1日 18:42:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/63204305.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定