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

2022-09-02 10:37:30

为了测试依赖于时间的代码,最好使用虚拟时钟模式

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

现在在Java中,我们有JodaTime和class,它允许设置采样时间DateTime

DateTimeUtils.setCurrentMillisFixed(today.getMillis());

并通过以下方式将固定时间重置为系统时间:

DateTimeUtils.setCurrentMillisSystem();

这是一篇关于如何将其与TestNG一起使用的好文章

现在问题来了!

如果此技术在全局上下文中为运行测试的时间全局设置固定时间,则将此技术与 setUp 和 tearDown 方法结合使用的安全性如何。只要我得到它 - 只要我们没有两个并发测试,这种技术在同一环境中并行运行,它就会起作用。


答案 1

必须确保在方法中调用 。因此,一个测试不会影响另一个测试。即使测试中发生异常,TestNG 也应该调用。DateTimeUtils.setCurrentMillisSystem()tearDowntearDown

当我想将类与 .我介绍一个接口和一个实现,如下所示:System.currentTimeMillis();ClockSystemClock

public interface Clock {
    public long getCurrentTimeMillis();
}

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

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

有些人可能会争辩说,引入这样一个接口来解耦一种方法是过度工程化的,这将对性能产生影响。但幸运的是,我们有一个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;
    }
}

默认构造函数用于“正常”使用,而另一个包作用域构造函数可用于单元测试。只需将测试类放在同一包中即可。

您也可以使用该界面,因为它是一个@FunctionalInterfaceClock

public class SomeClass {
    private Clock clock;

    public SomeClass(){
         this(System::currentTimeMillis);
    }

    public SomeClass(Clock clock){
        this.clock = clock;
    }
}

答案 2

推荐