在测试中使用DateTimeUtils.setCurrentMillisFixed是否安全



为了测试与时间相关的代码,最好使用虚拟时钟模式

我们的想法是,我们不使用new Date提取当前时间,而是从一个时钟中提取,该时钟可以用返回预定义固定时间的虚拟时钟来模拟。

现在在Java中,我们有带有DateTime类的JodaTime,它允许使用设置采样时间

DateTimeUtils.setCurrentMillisFixed(today.getMillis());

并使用将固定时间重置为系统时间

DateTimeUtils.setCurrentMillisSystem();

这里有一篇关于如何将它与TestNG一起使用的好文章。

现在问题来了!

如果在全局上下文中为运行测试的时间全局设置固定时间,那么将此技术与setUp和tearDown方法一起使用是多么安全。只要我得到了它——它只会在我们没有两个并行测试的情况下工作,而这项技术在同一个环境中并行运行。

必须确保在tearDown方法中调用DateTimeUtils.setCurrentMillisSystem()。这样一个测试就不会影响另一个测试。即使测试中出现异常,TestNG也应该调用tearDown

当我想将类与System.currentTimeMillis();解耦时,我通常更喜欢另一种方式。我介绍了一个接口Clock和一个类似的实现SystemClock

public interface Clock {
public long getCurrentTimeMillis();
}
public class SystemClock implements Clock {
public long getCurrentTimeMillis(){
return System.currentTimeMillis();
}
}

对于测试,可以很容易地创建一个mock,该mock在每次调用时返回一个固定的时间或一系列预定义的时间。

有些人可能会认为,引入这样一个接口来只解耦一个方法是过度设计的,这会对性能产生影响。但幸运的是,我们有一个JIT编译器,因为JIT知道只加载了SystemClock类,所以它知道(目前)不存在其他实现。在这个假设下,它可以只使用内联方法。

所以我更喜欢用测试最好的方式编写代码。

编辑

对于Java 8,您可能希望使用Supplier<Long>接口。

例如,在您的客户端代码中,您可以使用方法引用

public class SomeClass {
private Supplier<Long> currentTimeMillisSupplier;
public SomeClass(){
this(System::currentTimeMillis);
}
SomeClass(Supplier<Long> currentTimeMillisSupplier){
this.currentTimeMillisSupplier = currentTimeMillisSupplier;
}
}

默认构造函数用于"正常"使用,而其他包范围的构造函数可用于单元测试。只需将测试类放在同一个包中即可。

您也可以使用Clock接口,因为它是@FunctionalInterface

public class SomeClass {
private Clock clock;
public SomeClass(){
this(System::currentTimeMillis);
}
public SomeClass(Clock clock){
this.clock = clock;
}
}

相关内容

  • 没有找到相关文章

最新更新