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

时间:2014-01-06 11:44:01

标签: java junit testng jodatime

为了测试与时间相关的代码,最好使用Virtual Clock Pattern

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

现在在Java中我们有DateTime类的JodaTime,它允许用

设置采样时间
DateTimeUtils.setCurrentMillisFixed(today.getMillis());

使用以下命令将固定时间重置为系统时间:

DateTimeUtils.setCurrentMillisSystem();

以下是关于如何在TestNG中使用它的good article

现在问题了!

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

1 个答案:

答案 0 :(得分:17)

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

当我想要将一个类与System.currentTimeMillis();分离时,我常常采用另一种方式。我介绍了一个接口Clock和一个实现SystemClock,如下所示:

public interface Clock {
    public long getCurrentTimeMillis();
}

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

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

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

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

修改

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

E.g。在您的客户端代码中,您可以使用method references

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;
    }
}