/haste

A small library for testing time-related stuff

Primary LanguageJavaApache License 2.0Apache-2.0

Maven Central Build Status codecov

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.