Рубрики
Без рубрики

Контроль времени в Java

Время – хитрая штука, оно всегда меняется. Наличие таких движущихся частей в кодовой базе может быть очень… Помеченный как java, tdd, тест.

Время – хитрая штука, оно всегда меняется. Наличие таких движущихся частей в кодовой базе может быть очень раздражающим, например, при тестировании. В этой статье мы увидим, как контролировать время в 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”