出于某些测试目的,我想准确预测System.currentTimeMillis()
会返回什么。有什么方法可以冻结或手动设置System.currentTimeMillis()
调用时返回的内容?
6 回答
我强烈建议您避免在一般代码中使用System.currentTimeMillis
(new Date()
等)。
相反,创建一个Clock
表示“为您提供当前时间的服务”的接口,然后创建一个确实使用System.currentTimeMillis
或其他的实现,以及一个您可以显式控制的假实现。
使用依赖注入使该服务的实例可用于需要它的代码。在生产中,使用System.currentTimeMillis
版本,在测试中使用你的假货。
这使您不仅可以停止时间,还可以将其设置为您想要的任何东西 - 这样您就可以拥有您知道永远不会过期的静态测试数据,并且您可以轻松地测试边界周围的棘手事物等。我用过这个在许多项目中都非常成功,以至于在我的 Noda Time 项目中,这是获得“当前时间”的方式。
请注意,如果您在 Java 中花费大量时间,我建议您使用Joda Time,并让您的Clock
界面返回Instant
:
public interface Clock {
Instant now();
}
是的,这是可能的,但这是测试代码的味道。
您可以使用一个模拟库来模拟静态方法(如在此PowerMock 示例中),但您应该避免这样做,并按照其他答案的建议封装时间数据。
这就是测试的样子,使用PowerMock和Mockito:
@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
}
}
是否可以冻结 System.currentTimeMillis()?不
如果您希望您的代码能够被测试,您需要使用一种围绕该时间值的包装器
不,您不能设置或冻结System.currentTimeMills()。但是,如果您的要求是这样的,那么在这种情况下,您可以在变量中设置时间并在需要时使用。但是 System.currentTimeMills() 将始终以毫秒为单位返回当前时间值。
Java 8 中内置的新 java.time 包包括一个 java.time.Clock 接口“以允许在需要时插入备用时钟。改用它。
是的,存在解决方案:
出于测试目的,您可以创建自己的包装系统,例如
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() - 查看其他答案。