I have a question with regards to the question Time dependent unit tests
Let's say I build Spring application which contains service interface and its implementation
If I want to change clock in test, I would have to "pollute" production code and interface with e.g. setClock
method as follows:
public interface MyService {
void heavyBusinessLogic();
void setClock(Clock clock);
}
@Service
public class MyServiceImpl implements MyService {
private Clock clock = Clock.systemDefaultZone();
@Override
public void heavyBusinessLogic() {
if (LocalDate.now(clock)...) {
...
}
}
@Override
public void setClock(Clock clock) {
this.clock = clock;
}
}
In test, I can invoke, e.g.:
service.setClock(Clock.fixed(Instant.parse("2017-02-14T01:22:00Z"), ZoneOffset.UTC));
How can I abstract away such cross-cutting concern in Spring?
I want to stick with java.time.Clock (I don't want to use Joda)
Java Clock class is present in java. time package. It was introduced in Java 8 and provides access to current instant, date, and time using a time zone. The use of the Clock class is not mandatory because all date-time classes also have a now() method that uses the system clock in the default time zone.
A clock providing access to the current instant, date and time using a time-zone. Instances of this class are used to find the current instant, which can be interpreted using the stored time-zone to find the current date and time. As such, a clock can be used instead of System. currentTimeMillis() and TimeZone.
fixed() This method returns a clock that always returns the same instant. The main use case for this method is in testing, where the fixed clock ensures that tests are not dependent on the current clock.
Personally, I would simply add the clock in the constructor...
public MyServiceImpl(Clock clock) {
this.clock = clock;
}
...and perhaps add a nice default constructor...
public MyServiceImpl() {
this(Clock.systemDefaultZone());
}
This way you can get the default thing via spring and create a custom clock version manually, for example in your tests.
Of course, you could also forgo the default constructor and simply add a Clock
bean in your productive configuration, for example like this...
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
...which allows you to use a mocked Clock
as a bean in your test configuration, automatically allowing Spring to @Autowire
it via constructor injection.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With