Есть ли способ издеваться над временной меткой LocalDateTime, используя спецификации

Предположительно, я участвую в интеграционных тестах библиотеки, основанной на specs, с базовой реализацией, написанной на Scala. Тестирование больше привязано к потоку событий, протекающему с некоторым атрибутом timestamp.

Следующая заглушка является частью фактической реализации

private def eligibleForRecentPost(optionalPost: Option[SearchEntity]): Boolean = {
    optionalSearch.map(search => search.timestamp)
      .exists(searchTime => searchTime >= LocalDateTime.now()
        .minusDays(recencyDurationInDays).atZone(ZoneId.systemDefault).toInstant.toEpochMilli)
}

Теперь код, который я бы искал, может быть чем-то вроде

// just a mock
when(LocalDateTime.now().minusDays(any)
    .atZone(ZoneId.systemDefault).toInstant.toEpochMilli)
    .thenReturn(1579625874972)

Обратите внимание, я знаю, что search.timestamp в тесте может быть обновлен, но это потребует обновления событий после каждого recencyDurationInDays!!

Но есть ли лучший и надежный способ сделать это в specs2 и/или scala?

Правка. Должен отметить, что я не собираюсь менять реализацию таким образом, чтобы LocalDateTime был переопределен/обернут другим классом.


person Naman    schedule 05.05.2020    source источник
comment
Жаль, что вы не можете или не хотите изменять реализацию. Для всех, кто может, позвольте мне предположить, что ответ находится в этом вопросе: Написание и тестирование удобных методов с использованием классов Java 8 Date/Time. И/или здесь.   -  person Ole V.V.    schedule 05.05.2020
comment
@ОлеВ.В. Верно. Я просмотрел все такие ссылки на StackOverflow до публикации вопроса, и это также было причиной явного редактирования. Я хотел бы в идеале изменить реализацию, но это не всегда практически возможно. :)   -  person Naman    schedule 05.05.2020


Ответы (1)


Существуют такие инструменты, как powermock, которые позволяют такие вещи. Но это последнее средство, если вам нужно издеваться над чем-то в коде, над чем вы не можете контролировать.

Обычно вы вместо этого делаете что-то вроде:

trait Clock {

  def now(): LocalDateTime  
}

class DefaultClock extends Clock {

  def now(): LocalDateTime = LocalDateTime.now()
}

а затем внедрить экземпляр Clock в код, который его использует.

Вместо того, чтобы издеваться над статическим методом, вы можете просто передать экземпляр, который делает все, что вы хотите:

val simulatedNow = ... // calculate the right date for now
val clock = new Clock {
  def now() = simulatedNow
}
// inject clock
// run code and check assertion
person Mateusz Kubuszok    schedule 05.05.2020
comment
Это предложение также обновить фактический код? Если это так, мой плохой, я, должно быть, упомянул в вопросе, что это одно направление, которого я не жду. - person Naman; 05.05.2020
comment
Тогда ваша единственная надежда — powermock. Обычный макет не позволит вам имитировать статические методы. И даже с powermock это хрупко, поскольку это буквально требует, чтобы JVM модифицировала байт-код существующих классов вместо создания прокси-серверов, как это делают обычные моки. См. этот ответ для получения более подробной информации "> stackoverflow.com/questions/10583202/ - person Mateusz Kubuszok; 05.05.2020
comment
Java имеет свою собственную реализацию часов для такого рода проблем, и Cats тоже, это правильный способ кодирования, если вы хотите взломать, тогда делайте что угодно, но это правильный подход. - person Bruno; 06.05.2020