Время – хитрая штука, оно всегда меняется. Наличие таких движущихся частей в кодовой базе может быть очень раздражающим, например, при тестировании. В этой статье мы увидим, как контролировать время в Java.
Давайте возьмем простой пример службы доставки пиццы. Его политика заключается в том, что пицца должна быть доставлена в течение 10 минут. Когда клиент запрашивает доставку, время доставки автоматически рассчитывается системой на основе этой политики.
public class DeliveryPolicy { public Delivery createDelivery(Pizza pizza) { LocalDateTime deliveryTime = LocalDateTime.now().plus(10, ChronoUnit.MINUTES); return new Delivery(pizza, deliveryTime); } }
Как мы можем это проверить?
Поскольку время всегда меняется, мы не можем очень точно протестировать этот метод, не изменив его код. Мы могли бы проверить, что время доставки составит приблизительно 10 минут в будущем, но, как и все нечеткие тесты, время от времени он может давать сбои в зависимости от контекста, в котором он выполняется.
Сделайте Зависимость Видимой
Причина, по которой этот код так сложно протестировать, заключается в том, что у него есть скрытая зависимость: часы. Начиная с Java 8, все новые
методы API даты принимают часы в качестве аргумента. Затем мы можем сделать эту зависимость видимой.
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); } }
Внедрение зависимостей на помощь
Теперь, когда зависимость существует, мы можем внедрить ее в конструктор, как и любую другую зависимость, и тестирование этого метода становится тривиальным.
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); } }
Использование с пружинным каркасом и пружинным ботинком
Если вы используете Spring Framework в своем приложении, вы можете создать компонент Поставщик часов
, который будет указывать часы по умолчанию. Более того, если вы используете Spring Boot, это позволяет вам очень легко имитировать это в интеграционных тестах с аннотацией @MockBean
.
@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(); } }
Вывод
Раньше я думал, что время, как и случайность, очень трудно проверить. С API даты Java 8 это становится тривиальным. Вам просто нужно признать свою зависимость от часов и относиться к ней как к любой другой зависимости. Теперь я почти никогда не использую метод API даты без передачи часов в качестве параметра. Это позволяет мне очень легко контролировать время в приложении (в модульных, интеграционных или функциональных тестах).
Оригинал: “https://dev.to/rnowif/controlling-the-time-in-java-43kh”