DEV Community

Cover image for A way of testing code based on time in a Spring Boot Application
Sergio Garcia Moratilla
Sergio Garcia Moratilla

Posted on

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;
    }
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Image: https://unsplash.com/photos/FfbVFLAVscw

Top comments (1)

Collapse
 
grexe profile image
Gregor B. Rosenauer • Edited

very elegant solution with a conditional bean and provider - for a simple solution, you can also use the now() variants which accept a java.time.Clock as described in baeldung.com/java-override-system-..., and then when constructing the bean, set the "real" clock in normal code, or a fixed clock for testing, just as you did in your example.