尝试模拟时出现 MockitoException java.lang.System

MockitoException when trying to mock java.lang.System

我有一个模拟 java.lang.System class:

静态方法的测试用例
@Test
fun `getLocalTime()`() {
    // Arrange
    val staticMock = Mockito.mockStatic(System::class.java)
    Mockito.`when`(System.currentTimeMillis()).thenReturn(1000L)

    // Action
    val res = deviceTimeProvider.getLocalTime()

    // Assert
    Truth.assertThat(res).isEqualTo(1000L)

    staticMock.close()
}

但是当我运行测试时,我得到了这个错误:

org.mockito.exceptions.base.MockitoException: It is not possible to mock static methods of java.lang.System to avoid interfering with class loading what leads to infinite loops

为什么会这样?我如何模拟 java.lang.System class 的方法?

虽然 Mockito 自 3.4.0 version allows mocking static methods it is not allowed to mock the Thread and System static methods, see this comment on github

Finally note that Mockito forbids mocking the static methods of System (and Thread). Those methods are to much cemented into class loading which happens in the same thread. At some point, we might add instrumentation to class loading to temporarily disable the static mocks within it to make mocking these classes, too, where we also would need to disable their intensification properties. You can however easily mock Instant.now().

如果您喜欢丑陋的解决方案,您仍然可以使用 PowerMockito

模拟系统
@PrepareForTest(System.class)
public class TestCase {
    @BeforeClass
    public void setup() {
        PowerMockito.mockStatic(System.class);
        PowerMockito.when(System.currentTimeMillis()).thenReturn(1000L);
    }
...

但我会尽可能避免模拟系统 类。您仍然可以将其包装在方法中并模拟此方法。

在 Mockito 的帮助下模拟 java.lang.System class 的静态方法。

  1. 创建一个界面即ISystem.java
public interface ISystem {

    String getProperty(String name);

    Long getCurrentTimeInMillis();

}

2- 创建 ISystem 接口的实现 class 即 ISystemImpl.java

public class ISystemImpl implements ISystem {

    @Override
    public String getProperty(final String name) {
        return System.getProperty(name);
    }

    @Override
    public Long getCurrentTimeInMillis() {
        return System.currentTimeMillis();
    }

}

3- 在 DeviceTimeProvider.java class.

中使用 Isystem.java
public class DeviceTimeProvider {

   @NonNull private final ISystem mISystem;

   public DeviceTimeProvider(ISystem iSystem){
       mIsystem = iSystem;
   }

   public Long getLocalTime(){
       return mIsystem.getCurrentTimeInMillis()
   }

}

4- 现在终于在测试中模拟 ISystem 接口 class。

public class DeviceTimeProviderTest {

   private ISystem mISystem;
   private DeviceTimeProvider sut;

   @Before
   public setup(){
       mIsystem = mockito.mock(ISystem.class)
       sut = new DeviceTimeProvider(mISystem);
   }

   @Test
   public void getDeviceLocalTime(){
       Long expectedTime = 1000L;
       mockit.when(mISystem.getCurrentTimeInMillis()).thenReturn(expectedTime);
       
       Long actualTime = sut.getLocalTime();

       Assert.assertEquals(actualTime, expectedTime);
   }

}

输出