【问题标题】:Is it possible to freeze System.currentTimeMillis() for testing是否可以冻结 System.currentTimeMillis() 进行测试
【发布时间】:2013-07-01 06:03:59
【问题描述】:

出于某些测试目的,我想准确预测System.currentTimeMillis() 将返回什么。有什么方法可以冻结或手动设置调用System.currentTimeMillis() 时将返回的内容?

【问题讨论】:

  • 你不能只将结果存储在一个变量中,并在需要时重用它吗?
  • 如果您想覆盖行为,您必须将对 System.currentTimeMillis() 的所有调用替换为调用它或返回存储值的方法。
  • 如果你入侵了 JVM,则不会。
  • 除非你是查尔斯·泽维尔

标签: java testing


【解决方案1】:

我强烈建议您避免在通用代码中使用System.currentTimeMillis(和new Date() 等)。

相反,创建一个 Clock 接口,表示“为您提供当前时间的服务”,然后创建一个确实使用 System.currentTimeMillis 或其他内容的实现,以及一个您可以使用的假实现显式控制。

使用依赖注入使该服务的实例可供需要它的代码使用。在生产中使用System.currentTimeMillis 版本,在测试中使用你的假的。

这使您不仅可以停止时间,还可以将其设置为您想要的任何内容 - 这样您就可以拥有您知道永远不会过期的静态测试数据,并且您可以轻松地测试边界周围的棘手事物等。我我在许多项目中都非常成功地使用了这种方法,以至于在我的 Noda Time 项目中,它是获取“当前时间”的方式。

请注意,如果您在 Java 中花费大量时间,我建议您使用 Joda Time,并让您的 Clock 接口返回 Instant

public interface Clock {
    Instant now();
}

【讨论】:

  • 在相关说明中:我真的很期待 java.time(又名 JSR-310)最终到达 Java 8。
【解决方案2】:

是的,有可能,但这是测试代码的味道。

您可以使用一个模拟库来模拟静态方法(如PowerMock example),但您应该避免这样做,并按照其他答案的建议封装时间数据。

这就是测试的样子,使用 PowerMockMockito

@RunWith(PowerMockRunner.class)
@PrepareForTest(System.class)
public class TestTime {

    @Test
    public void testTime() {
        PowerMockito.mockStatic(System.class);
        PowerMockito.when(System.currentTimeMillis()).thenReturn(42l);
        System.out.println(System.currentTimeMillis()); //prints 42

        //your test code here
    }

}

【讨论】:

  • @PeterLawrey:不,那是小写的L,而不是1
  • :D。我真的最终应该重新训练自己改用L。哦,好吧,让我们称之为代码阅读技能培训奖金。
  • 你真的可以模拟 that 方法吗?我很惊讶!
  • 您可以使用 PowerMock 模拟大多数方法(不确定native)。当然,这并不意味着您应该,除非您正在处理遗留代码怪物或类似的东西。
【解决方案3】:

是否可以冻结 System.currentTimeMillis()?

如果您希望您的代码能够被测试,您需要对该时间值使用某种包装器

【讨论】:

    【解决方案4】:

    不,您不能设置或冻结System.currentTimeMills()。 但是,如果您的要求是这样的,那么在这种情况下,您可以在变量中设置时间并在需要时使用。但是 System.currentTimeMills() 将始终以毫秒为单位返回当前时间值。

    【讨论】:

      【解决方案5】:

      Java 8 中内置的新 java.time 包包括一个 java.time.Clock 接口“允许在需要时插入备用时钟。改用它。

      【讨论】:

        【解决方案6】:

        是的,解决方案存在:

        出于测试目的,您可以创建自己的包装系统,例如

        package my.pack;
        
        import java.io.PrintStream;
        
        public class System
        {
            // reuse standard System.out:
            public static PrintStream out = java.lang.System.out;
        
            // do anything with chosen method
            public static long currentTimeMillis()
            {
                return 1234567;
            }
        
        }
        

        并将其导入您要测试的类中

        import my.pack.System;
        

        在这种情况下,所有对 System 的调用都将通过您自己的 System 类传递。

        注意:

        • 这是“如何拦截System.currentTimeMillis()”的解决方案

        • 适合自动测试

        • 不是“如何设计好程序”的示例。如果您要求一个好的设计,那么您需要重构您的代码,替换 System.currentTimeMillis() - 查看其他答案。

        【讨论】:

        • 您是否建议仅在测试期间修改被测类的源代码?这意味着您测试的有效类不是您在生产中实际运行的类!
        • 是的,您需要修改您的类以进行测试(附加一行),是的,对于生产,您需要删除该导入。但是,是的,您可以改变行为,这是一种简单的方法
        • 除了测试与您正在部署的类不同的类的问题之外,我还认为“简单”的方式:以一种理智的方式自动化这将是一件痛苦的事情。
        • 当然,此解决方案不适用于自动测试。 “简单”是指更少的重构:您无需使用 currentTimeMillis() 更改所有行,无论它们在哪里
        猜你喜欢
        • 2021-01-18
        • 1970-01-01
        • 2016-05-15
        • 2018-06-26
        • 2020-10-31
        • 2017-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多