为了测试与时间相关的代码,最好使用Virtual Clock Pattern
我们的想法是,我们不使用new Date
来提取当前时间,而是从可以使用虚拟时钟模拟的时钟返回预定义的固定时间。
现在在Java中我们有DateTime
类的JodaTime,它允许用
DateTimeUtils.setCurrentMillisFixed(today.getMillis());
使用以下命令将固定时间重置为系统时间:
DateTimeUtils.setCurrentMillisSystem();
以下是关于如何在TestNG中使用它的good article。
现在问题了!
如果在运行测试时全局设置全局上下文中的固定时间,则将此技术与setUp和tearDown方法一起使用是多么安全。只要我得到它 - 它只会在我们没有两个并行测试时才能工作,这种技术在同一环境中并行运行。
答案 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;
}
}