【问题标题】:JMockit mocking System.currentTimeMillis()JMockit 模拟 System.currentTimeMillis()
【发布时间】:2015-07-31 15:47:48
【问题描述】:

运行此测试:

@Test
public void testSystemCurrentTimeMillis() {
    new NonStrictExpectations(System.class) {{
        System.currentTimeMillis(); result = 1438357206679L;
    }};
    long currentTime = System.currentTimeMillis();
    assertEquals(1438357206679L, currentTime);
}

我得到一个 IllegalStateException:

java.lang.IllegalStateException:此时缺少对模拟类型的调用;请确保此类调用仅在声明合适的模拟字段或参数之后出现 在 unittests.DateTest$1.(DateTest.java:24) 在 unittests.DateTest.testSystemCurrentTimeMillis(DateTest.java:23)

我的测试 (JMockit 1.18) 有什么问题?

【问题讨论】:

  • 什么是结果?你能分享整个课程代码吗?还有什么是 NonStrictExpectations ???你也可以分享一下代码吗?
  • NonStrictExpectations 是一个 JMockit 类。结果用于 JMockit 模拟。 jmockit.org/tutorial/Mocking.html#expectation
  • 我总是使用 org.joda.time.DateTimeUtils 来获取当前时间 DateTimeUtils.currentTimeMillis() 并在单元测试 DateTimeUtils.setCurrentMillisFixed(longValue) 结束时使用 DateTimeUtils.setCurrentMillisSystem()
  • 我想用 JMockit 来做。

标签: java unit-testing jmockit


【解决方案1】:

我的最终解决方案是为System创建一个MockUp,它只模拟currentTimeMillis()方法:

private static class SystemMock extends MockUp<System> {
    @Mock
    public static long currentTimeMillis() {
        return 10000000L;
    }
}

@Test
public void testAddNowDate() {
    new SystemMock();
    long currentTime = System.currentTimeMillis();
    assertEquals(10000000L, currentTime);
}

【讨论】:

  • 是的,这是这种情况下的最佳解决方案。也就是说,您应该尽量避免模拟系统时钟;在测试中模拟它的需要暗示了被测代码中的设计问题。
  • @Rogério 需要详细说明吗?如何在不模拟的情况下测试依赖于系统时钟的功能?
  • 需要当前时间的代码应该使用更高级别的类型(java.util.Datejava.time.LocalDate等),而不是currentTimeMillis()。然后它应该允许从客户端代码传入日期/时间对象,而不是总是获取当前时间。当然,除非您真的需要以毫秒/纳秒为单位的当前系统时钟时间。正如我所说,调用System.currentTimeMillis() 很可能是一个设计问题,但如果没有更多信息,我无法确定。
  • 一个用例是制作一个模拟测试用例,用 currentTimeMillis 甚至 nanoTime 检查经过的时间。如果你模拟它,你可以检查边界情况。我试过了,但它在我的案例中涉及太多了。太多 OOM 错误等。
  • 是的!互联网上的所有内容都说在模拟 System 时存在设计问题,但这就像说我们永远不应该使用 System 这不是真的。有时会出现设计问题。有时你无法改变所说的设计。有时你只需要帮助来模拟系统而不管设计如何。
【解决方案2】:

就像 JMockit 的许多事情一样,它很容易做到。试试这个..

@Test
public void testSystemCurrentTimeMillis(@Mocked final System unused) {
    new NonStrictExpectations() {{
        System.currentTimeMillis(); result = 1438357206679L;
    }};
    long currentTime = System.currentTimeMillis();
    assertEquals(1438357206679L, currentTime);
}

顺便说一句,发现this 网站是一个很好的参考。可能你被静态方法绊倒了。您需要做的就是将具有静态方法的类声明为已模拟的类——您永远不需要引用该变量,因此我将其命名为“未使用”。

【讨论】:

  • 接受了你的回答,但我只想模拟 currentTimeMillis() 方法,而不是整个 System 类。似乎是 JMockit 中的一个错误。
  • @trunkc 这不是错误,而是“部分模拟”功能的限制:它无法处理 native 之类的 System.currentTimeMillis() 方法。但是,此限制仅适用于 Expectations API;您可以通过创建 MockUp&lt;System&gt;(使用 Mockups API)来模拟所需的 native 方法。
  • 您应该在教程中添加有关此限制的提示。
【解决方案3】:

JMockit 1.17 版中引入了这个东西,仅用于将对象引用与您的 NonStrictExpectation(){} 块一起使用,已弃用 Mocked 的属性值

不推荐使用@Mocked 的“值”属性,该属性用于“静态”部分模拟。现有用途应替换为“动态”部分模拟,方法是在调用 Expectations(Object...) 构造函数时将实例或类传递给部分模拟,或者应用 MockUp 类。

请参考以下链接:JMockit version history

【讨论】:

  • 我没有使用@Mocked的value属性。
【解决方案4】:

是的,这是部分嘲弄。通过对 nonStrictExpectation(){} 进行小幅修正,您的上述代码也可以得到修复:

@Mocked
private System system;

new NonStrictExpectations() {{
    System.currentTimeMillis(); 
    result = 1438357206679L;
}};

long currentTime = System.currentTimeMillis();
assertEquals(1438357206679L, currentTime);

这也应该有效。

【讨论】:

  • 这个例子没有使用部分模拟。
【解决方案5】:

我为此使用 PowerMock:

private void configureSystemClockMock(final int clockJump) {
    AtomicInteger callCount = new AtomicInteger();

    Answer<Integer> answer = invocation -> {
        callCount.addAndGet(1);
        return callCount.get() * clockJump;
    };

    PowerMockito.mockStatic(System.class);
    PowerMockito.doAnswer(answer).when(System.class);
    System.currentTimeMillis();
}

当然,您可以在调用中添加任何您想要的内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-09
    • 1970-01-01
    • 2015-11-13
    • 2021-01-07
    • 2014-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多