11

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

4

6 回答 6

27

我强烈建议您避免在一般代码中使用System.currentTimeMillisnew Date()等)。

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

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

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

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

public interface Clock {
    Instant now();
}
于 2013-07-01T06:12:30.307 回答
18

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

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

这就是测试的样子,使用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
    }

}
于 2013-07-01T06:14:45.340 回答
1

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

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

于 2013-07-01T06:05:15.133 回答
1

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

于 2013-07-01T06:08:41.817 回答
1

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

于 2017-08-28T19:25:14.673 回答
-5

是的,存在解决方案:

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

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() - 查看其他答案。

于 2013-07-01T06:28:01.623 回答