Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to abstract away java.time.Clock for testing purposes in Spring

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)

like image 283
Patrik Mihalčin Avatar asked May 02 '17 13:05

Patrik Mihalčin


People also ask

Is clock class final in Java?

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.

What is Java time clock?

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.

What is a fixed clock?

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.


1 Answers

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.

like image 120
Florian Schaetz Avatar answered Oct 24 '22 13:10

Florian Schaetz