尝试模拟java.lang.System时发生MockitoException

时间:2020-10-03 08:44:09

标签: java android unit-testing mocking mockito

我有一个测试用例,它模拟java.lang.System类的静态方法:

@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:无法执行 模拟java.lang.System的静态方法以避免干扰 类加载导致无限循环的原因

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

3 个答案:

答案 0 :(得分:2)

虽然Mockito自3.4.0版本以来就允许模拟静态方法,但不允许模拟ThreadSystem静态方法,请参见this comment on github

最后请注意,Mockito禁止模拟System(和Thread)的静态方法。这些方法在同一个线程中进行了类加载。有时,我们可能会在类加载中添加工具,以暂时禁用其中的静态模拟,从而对这些类进行模拟,在此我们还需要禁用其强化属性。但是,您可以轻松地模拟Instant.now()。

答案 1 :(得分:1)

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

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

但是如果可能的话,我会避免模拟System类。您仍然可以将其包装在方法中并模拟该方法。

答案 2 :(得分:0)

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

  1. 创建一个接口,即 ISystem.java
public interface ISystem {

    String getProperty(String name);

    Long getCurrentTimeInMillis();

}

2- 创建 ISystem 接口的实现类,即 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- 在 Isystem.java 类中使用 DeviceTimeProvider.java

public class DeviceTimeProvider {

   @NonNull private final ISystem mISystem;

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

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

}

4- 现在终于模拟了测试类中的 ISystem 接口。

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

}

输出

enter image description here