java,  test,  time

A way of testing code based on time in a Spring Boot Application

A way of testing code based on time in a Spring Boot Application

Every time I see a Instant.now() / new Date() / something that creates a date based on the current time I tremble. How do you expect to test that in a simple / coherent / easy to follow way?

When I see that code, I usually see tests using now() + duration to check that everything works. So that the test is not the same when run today than when run tomorrow.

Wouldn’t it better to be able to “fix” the time and test your code with exact values of times/periods as well?

So that I’ve fighting in my team in order that everyone uses a ClockProvider to get the “current” time. That way I can instance my own ClockProvider in unit tests and I can override the default ClockProvider of my application in integration tests.

The former is pretty easy to understand, so that I’m not writing an example of that. This is an example of the latter.

For instance, my application would look like this:

@SpringBootApplication
public class BookingApplication extends AbstractAnemoneApplication {

    @Bean
    @ConditionalOnMissingBean(ClockProvider.class) // to allow tests to overwrite it
    public ClockProvider getClockProvider() {
        return () -> Clock.systemDefaultZone();
    }

    // An example of method using ClockProvider
    boolean isItJanuary(@Nonnull ClockProvider clockProvider) {
        return clockProvider.getClock().instant().atZone(UTC).getMonth() == Month.JANUARY;
    }

And my IT tests:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
    FixingClockConfiguration.class, BookingsApplication.class})
@ExtendWith(SpringExtension.class)
public class BookingsApplicationIT {

    @TestConfiguration
    static class FixingClockConfiguration {

        @Bean
        public ClockProvider getClockProvider() {
            ZonedDateTime fixedInstant = ZonedDateTime.of(2019, 04, 15, 14, 00, 00, 00, UTC);
            return () -> Clock.fixed(fixedInstant.toInstant(), fixedInstant.getZone());
        }
    }

// ... your tests based on that date
}