A small library for testing time-related stuff
In a few applications I was struggling with time aspect during tests. It's not hard to write some kind of proxy or mocks to provide a proper date but it is annoying to write them every time. Here comes the idea to create an open source library to help write tests based on the passage of time and also to help write more testable systems.
<dependency>
<groupId>io.github.krasnoludkolo</groupId>
<artifactId>haste</artifactId>
<version>0.3.1</version>
</dependency>
compile 'io.github.krasnoludkolo:haste:0.3.1'
Haste provides:
- the implementation of ScheduledExecutorService
with
advanceTimeBy
method to simulate lapse of time. - the interface
TimeSource
to create abstraction over time
Some actions in your system may also plan another actions to be done in the future.
E.g. when you add a sport fixture you may want to check the result after it has finished
When using normal java scheduler it is hard to scheduledExecutorServiceWithMovableTime results of scheduled jobs without e.g. mocking.
Here comes the implementation of ScheduledExecutorService with advanceTimeBy
method.
class FooTest{
private static final Runnable EMPTY_RUNNABLE = () -> {};
private static final Callable<Integer> RETURN_ONE_CALLABLE = () -> 1;
@Test
void shouldExecuteAllScheduledJobs() throws ExecutionException {
var executorService = Haste.ScheduledExecutionService.withFixedClockFromNow();
var schedule1 = executorService.schedule(RETURN_ONE_CALLABLE, 1, TimeUnit.SECONDS);
var schedule2 = executorService.schedule(EMPTY_RUNNABLE, 2, TimeUnit.SECONDS);
var schedule3 = executorService.schedule(EMPTY_RUNNABLE, 3, TimeUnit.SECONDS);
var schedule4 = executorService.schedule(EMPTY_RUNNABLE, 5, TimeUnit.SECONDS);
executorService.advanceTimeBy(4, TimeUnit.SECONDS);
assertEquals(Integer.valueOf(1), schedule1.get()); //not null
assertTrue(schedule1.isDone());
assertTrue(schedule2.isDone());
assertTrue(schedule3.isDone());
assertFalse(schedule4.isDone());
}
}
With Haste comes the fallowing interface
public interface TimeSource {
ZonedDateTime now();
//and more version of "now"
}
If you only need access to current time, without whole ScheduledExecutionService
staff, you can use MovableTimeSource
which extends TimeSource
interface. It simply works like in example
class MovableTimeSourceTest {
@Test
void shouldNowReturnTimeWithOffset() {
Instant instant = Instant.ofEpochMilli(0);
ZoneId zoneId = ZoneId.systemDefault();
Clock clock = Clock.fixed(instant, zoneId);
MovableTimeSource timeSource = Haste.TimeSource.withFixedClock(clock);
timeSource.advanceTimeBy(1, TimeUnit.HOURS);
ZonedDateTime now = timeSource.now();
ZonedDateTime expected = ZonedDateTime.now(clock).plusHours(1);
assertEquals(expected, now);
}
}
ScheduledExecutorServiceWithMovableTime
from Haste implements that interface so you can obtain 'moved'
time like in example
class Event{
private ZonedDateTime eventTime;
private TimeSource timeSource;
Event(ZonedDateTime eventTime, TimeSource timeSource) {
if (timeSource.now().isAfter(eventTime)) {
throw new IllegalArgumentException("Cannot make event in past");
}
this.eventTime = eventTime;
this.timeSource = timeSource;
}
boolean hasAlreadyBegun(){
return timeSource.now().isAfter(eventTime);
}
}
class EventTest {
@Test
void shouldEventStartsAfterStartDate() {
BlockingScheduledExecutionService service = BlockingScheduledExecutionService.withFixedClockFromNow();
ZonedDateTime eventTime = ZonedDateTime.now().plusHours(1);
Event event = new Event(eventTime, service);
service.advanceTimeBy(2, TimeUnit.HOURS);
assertTrue(event.hasAlreadyBegun());
}
}
Keep in mind that Haste is in early-alpha phase which means that some API details may change between versions.