gargoyle image Collected works and current thoughts

Time Travel

Getting time under control in Spring, one way

Overview

Time is often something worth raising to a domain-level concept is a recurring theme.

We’ve recently been doing some work where dates and times are important. We have cases where we want to verify that the time of creation is reflected correctly.

In Java 8 there are several options. Baeldung has a great article on those options. What follows is taking one of the options from that article all the way into a Spring-based project.

Background

In a system where time is important to the business problem, and you want to write tests where the actual time used is part of what you are checking, then getting time under control is a system requirement. It’s fair to say that system time is a domain-level concept.

Normally, Time is an external dependency because it is based on the clock in the computer upon which the app is running. Tests attempt to get every dependency under the control of the test. While not always possible, it is an ideal to strive for.

Java 8

Java 8 added an entirely new date/time implementation based on Joda Time and written by its author. The new and old co-exist. But they are fairly disconnected.

What follows are the moving parts to time travel in a spring-based test.

How to get time

For a component that needs time, instead of using Date, useClock. For example, instead of:

new Date()  // harder to control

We use this:

Date.from(systemClock.instant())
// or
Date.from(Instant.now(systemClock))

How to use the clock we define

Consider a Spring component that cares about time:

@Component
public class HelloComponent {
    private final Clock systemClock;
    public HelloComponent(Clock systemClock) {
        this.systemClock = systemClock;
    }
    public String message() {
        return "Hello, it is now: " + Date.from(systemClock.instant());
    }
}

Part of Application Configuration

For systemClock this to be injected, there needs to be a bean in the Spring configuration. The special sauce is a combination of two annotations:

This can be on its own class or, in this simple example, placed on the@SpringBootApplication directly.

@SpringBootApplication
@Configuration
public class TimeApplication {
	public static void main(String[] args) {
		SpringApplication.run(TimeApplication.class, args);
	}
	@Bean
	public Clock systemClock() {
		return Clock.systemDefaultZone();
	}
}

Not this Now, That Now

What about testing for a date that is not “now” in the real world?

Override the Clock

If you can get away with using a plain JUnit test, then you can construct your ownClockinstance, and manually call the constructor of theHelloComponent. That’s straightforward and a good option.

If you’re trying to get it to work in a spring context, it requires another moving part. We’ll override the application-generated Clock with a test Clock.

Create a Test Config

public class TestConfig {
    @Bean
    @Primary
    public Clock systemClock() {
        return Clock.fixed(
                Instant.parse("2031-08-22T10:00:00Z"),
                ZoneOffset.UTC);
    }
}

Oh yes, how about a test

@SpringBootTest(classes = {HelloComponent.class, TestConfig.class})
class HelloComponentTest {
    @Autowired
    HelloComponent hello;
    @Test
    void messageControlled() {
        String message = hello.message();
        assertThat(message).contains("2031");
    }
}

If you look at the test config, the date is fixed to 2031-08-22, so while the assertion could be more specific, it demonstrates that we are overriding the real time with our desired time.

Conclusion

Published 18 February 2021

" Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.