In order to test time-dependent code it's good to use Virtual Clock Pattern
The idea is that we pull current time not using new Date
, but from a clock which can be mocked with virtual clock returning predefined fixed time.
Now in Java we have JodaTime with DateTime
class and which allows to set sample time with
DateTimeUtils.setCurrentMillisFixed(today.getMillis());
And reset the fixed time to a system time with:
DateTimeUtils.setCurrentMillisSystem();
Here is a good article on how to use it with TestNG.
Now the question!
How safe it's to use this technique with setUp and tearDown methods if it globally sets fixed time in global context for the time of running test. As long as I get it - it will only work as long as we don't have two concurrent tests with this technique running in the same environment in parallel.
You must ensure that DateTimeUtils.setCurrentMillisSystem()
is invoked in the tearDown
method. So that one test does not affect another. TestNG should invoke tearDown
even if an exception occurs in your test.
I often prefer another way when I want to decouple a class from System.currentTimeMillis();
. I introduce an interface Clock
and one implementation SystemClock
like this:
public interface Clock {
public long getCurrentTimeMillis();
}
public class SystemClock implements Clock {
public long getCurrentTimeMillis(){
return System.currentTimeMillis();
}
}
For the tests it is then easy to create a mock that either returns a fixed time on every invocation or a series of predefined times.
Some might argue that it is overengineering to introduce such an interface to decouple only one method and it would be a performance impact. But fortunately we have a JIT compiler and since JIT knows that only the SystemClock
class is loaded it knows that no other implementation exist (at the moment). At this assumption it can just use inline method.
So I prefer to write code in the way it can be tested best.
EDIT
With Java 8 you might want to use the Supplier<Long>
interface.
E.g. in your client code you can than use method references
public class SomeClass {
private Supplier<Long> currentTimeMillisSupplier;
public SomeClass(){
this(System::currentTimeMillis);
}
SomeClass(Supplier<Long> currentTimeMillisSupplier){
this.currentTimeMillisSupplier = currentTimeMillisSupplier;
}
}
The default constructor is for 'normal' use, while the other package scoped constructor can be used for unit tests. Just place the test class in the same package.
You can also use the Clock
interface, because it is a @FunctionalInterface
.
public class SomeClass {
private Clock clock;
public SomeClass(){
this(System::currentTimeMillis);
}
public SomeClass(Clock clock){
this.clock = clock;
}
}