如何使用 PowerMockito 模拟 System.exit?

How to mock System.exit with PowerMockito?

我想对调用 System.exit(-1) 的代码 Java 进行单元测试,并希望它不执行任何操作而不是退出进程。根本原因是 JaCoCo 无法正常工作,项目指南希望看到该行被覆盖。更改经过测试的代码也不是一种选择。对系统的其他调用应该正常工作。 PowerMockito 2.0.7 已经在项目中使用,这里也应该使用。我当前的 Java 版本是 Windows.

上的 1.8.0_181

我试过

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

好像不行,System.exit好像还是退出了进程。 它是如何工作的?

我认为您应该替换示例代码中的两行

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

PowerMockito.mockStatic(System.class);

此更改在我的本地有效,因为 System.exit 由于静态方法上的 mock 不执行任何操作。

此外,我希望您正在使用 PrepareForTest 注释

@PrepareForTest(CLASS_UNDER_TEST)

间谍方法是调用真正的方法并对非静态方法进行一些包装。由于您需要模拟静态方法,因此应改用 mockStatic 方法。

更新 1

PowerMockito mockStatic 方法默认为 class 中的所有静态方法创建 mock。我没有任何干净的解决方案。但是,我可以建议一个看起来丑陋但可以满足需要的解决方案,即仅模拟特定的静态方法,其余方法调用真实方法。 PoweMockito 的 mockStatic 方法在内部调用 DefaultMockCreator 来模拟静态方法。

@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
  }
}

根据 PowerMockito 文档,调用 static void 方法的正确方法是 -

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

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

这应该为特定的静态 void 方法创建模拟行为。不幸的是,这对 System Class 不起作用,因为 System class 是最终的。如果它不是最终的,这会奏效。我试过了,我得到了这个异常 -

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

代码-

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