When testing an application, it's often useful to define a fake system clock in order to exercise code that uses dates.
While it's always possible to change the system clock directly, many view that style as undesirable:
- it affects all programs running on a machine, and not just the application being tested
- it can be time consuming and annoying to repeatedly change the system clock while testing
Instead of changing the system clock, it's possible to define a fake system clock just for your application. In production, the fake system clock returns normal time. During testing, the fake system clock is changed to return any time you need for effective test coverage. Of course, your application code will always need to reference the fake system clock, and never the real one, in order for this to work.
Example
The TimeSource interface allows you to define various implementations of a fake system clock:
public interface TimeSource { /** Return the system time. */ long currentTimeMillis(); }
This implementation mimics a system clock running one day in advance:
public final class TimeSrc implements TimeSource { /** One day in advance of the actual time.*/ @Override public long currentTimeMillis() { return System.currentTimeMillis() + ONE_DAY; } private static final long ONE_DAY = 24*60*60*1000; }
Using various TimeSource implementations, you can mimic any desired behavior for a system clock. Possible behaviors include:
- skip ahead to the future
- go back to the past
- use a fixed date, and a fixed time
- use a fixed date, but still let the time vary
- increment by one second each time you 'look' at the clock
- change the rate at which time passes, by speeding up or slowing down by a certain factor
- use the normal system clock without alteration
For this to work, an application must avoid calling these items directly:
- System.currentTimeMillis()
- the default constructor for the Date class (which in turn uses System.currentTimeMillis())
According to your needs, you may have to use the fake system clock in some or all of these places:
- your application code
- your logging output
- your framework classes
- your database settings
It's simple to configure the JDK logger to use your fake system clock.
A simple custom
Formatter
can use your TimeSource to alter the time of the LogRecord:
import java.util.logging.LogRecord; import java.util.logging.SimpleFormatter; public final class SimpleFormatterTimeSource extends SimpleFormatter { @Override public String format(LogRecord aLogRecord) { aLogRecord.setMillis(fTimeSource.currentTimeMillis()); return super.format(aLogRecord); } private TimeSource fTimeSource = BuildImpl.forTimeSource(); }