haste
A small library for testing time-related stuff
Goals and motivation
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.
Usage
<dependency>
<groupId>io.github.krasnoludkolo</groupId>
<artifactId>haste</artifactId>
<version>0.3.1</version>
</dependency>
compile 'io.github.krasnoludkolo:haste:0.3.1'
Features
TL;DR
Haste provides:
- the implementation of ScheduledExecutorService
with
advanceTimeBy
method to simulate lapse of time. - the interface
TimeSource
to create abstraction over time
Full version
Test scheduled tasks
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());
}
}
Get access to current time
With Haste comes the fallowing interface
public interface TimeSource {
ZonedDateTime now();
//and more version of "now"
}
Standalone time source
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);
}
}
ScheduledExecutionService as time source
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());
}
}
Disclaimer
Keep in mind that Haste is in early-alpha phase which means that some API details may change between versions.