DEV Community

Renaud Humbert-Labeaumaz
Renaud Humbert-Labeaumaz

Posted on • Originally published at rnowif.github.io on

Controlling the Time in Java

Time is a tricky thing, it’s always changing. Having such moving parts into the codebase can be very annoying when testing, for instance. In this article, we will see how to control the time in Java.

Let’s take a simple example of a pizza delivery service. Its policy is that pizzas should be delivered in 10 minutes. When the customer asks for a delivery, the delivery time is automatically calculated by the system based on this policy.

public class DeliveryPolicy {
  public Delivery createDelivery(Pizza pizza) {
    LocalDateTime deliveryTime = LocalDateTime.now().plus(10, ChronoUnit.MINUTES);

    return new Delivery(pizza, deliveryTime);
  }
}

How Do we Test it?

Since the time always changes we cannot test this method very precisely without changing its code. We could test that the delivery time is approximatively 10 minutes in the future but like all fuzzy tests, it could fail from time to time depending on the context on which it is executed.

Make Dependency Visible

The reason why this code is so difficult to test is that it has a hidden dependency: the clock. Since Java 8, all now methods of the Date API take a clock as an argument. We can then make this dependency visible.

public class DeliveryPolicy {

  private final Clock clock;

  public DeliveryPolicy() {
    this.clock = Clock.systemDefaultZone();
  }

  public Delivery createDelivery(Pizza pizza) {
    LocalDateTime deliveryTime = LocalDateTime.now(clock).plus(10, ChronoUnit.MINUTES);

    return new Delivery(pizza, deliveryTime);
  }
}

Dependency Injection to the Rescue

Now that the dependency is out there, we can inject it in the constructor, like any other dependency, and testing this method becomes trivial.

public class DeliveryPolicy {

  private final Clock clock;

  public DeliveryPolicy(ClockProvider clockProvider) {
    this.clock = clockProvider.get();
  }

  public Delivery createDelivery(Pizza pizza) {
    LocalDateTime deliveryTime = LocalDateTime.now(clock).plus(10, ChronoUnit.MINUTES);

    return new Delivery(pizza, deliveryTime);
  }
}
public class DeliveryPolicyTest {

  @Test
  public void should_schedule_delivery_ten_minutes_later() {
    ZonedDateTime now = ZonedDateTime.of(LocalDateTime.of(2017, 7, 18, 0, 0, 0), ZoneId.of("+01"));
    DeliveryPolicy policy = new DeliveryPolicy(() -> Clock.fixed(now.toInstant(), now.getZone()));

    Delivery delivery = policy.createDelivery(new Pizza());

    LocalDateTime tenMinutesLater = LocalDateTime.of(2017, 7, 18, 0, 10, 0);
    assertThat(delivery.getDeliveryTime()).isEqualTo(tenMinutesLater);
  }
}

Usage with Spring Framework and Spring Boot

If you use Spring Framework in your application, you can create a ClockProvider bean that will give the default Clock. Moreover, if you use Spring Boot, it allows you to mock this bean very easily in integration tests with the @MockBean annotation.

@RunWith(SpringRunner.class)
@SpringBootTest
public class DeliveryPolicyIT {

  @MockBean
  private ClockProvider clockProvider;

  @Autowired
  private DeliveryPolicy policy;

  @Test
  public void should_schedule_delivery_ten_minutes_later() {
    ZonedDateTime now = ZonedDateTime.of(LocalDateTime.of(2017, 7, 18, 0, 0, 0), ZoneId.of("+01"));
    Mockito.when(clockProvider.get()).thenReturn(Clock.fixed(now.toInstant(), now.getZone()));

    Delivery delivery = policy.createDelivery(new Pizza());

    LocalDateTime tenMinutesLater = LocalDateTime.of(2017, 7, 18, 0, 10, 0);
    assertThat(delivery.getDeliveryTime()).isEqualTo(tenMinutesLater);
  }
}
@Configuration
public class ClockConfig {

  @Bean
  public ClockProvider clockProvider() {
    return () -> Clock.systemDefaultZone();
  }
}

Conclusion

I used to think that the time, like random, was a very difficult thing to test. With the Java 8 Date API, it becomes trivial. You just have to acknowledge the dependency you have on the clock and treat it like any other dependency. Now, I almost never use a method of the Date API without passing a clock as a parameter. This allows me to control the time throughout the application very easily (in unit, integration or functional tests).

Top comments (3)

Collapse
 
skuzzle profile image
Simon Taddiken

Is there any particular reason why you introduced the ClockProvider instead of directly injecting the Clock instance? This seems like an extra level of indirection that does not bring much advantages here?

Collapse
 
rnowif profile image
Renaud Humbert-Labeaumaz • Edited

You are correct, it does not bring many advantages here. I guess I did that in order to avoid passing around JDK objects and encapsulate this in my own wrapper. It is more of an intuition than a well-thought decision ^_^

Collapse
 
rnowif profile image
Renaud Humbert-Labeaumaz

Thank you very much!
I am very glad it helped you :-)