В Joda Time есть приятный DateTimeUtils.setCurrentMillisFixed () для имитации времени.
Это очень практично в тестах.
Есть ли эквивалент в Java 8 java.time API ?
В Joda Time есть приятный DateTimeUtils.setCurrentMillisFixed () для имитации времени.
Это очень практично в тестах.
Есть ли эквивалент в Java 8 java.time API ?
Самое близкое - это Clock
объект. Вы можете создать объект Clock, используя любое время, которое хотите (или текущее время системы). Все объекты date.time имеют перегруженные now
методы, которые вместо текущего времени принимают объект часов. Таким образом, вы можете использовать инъекцию зависимостей, чтобы ввести часы с определенным временем:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
См. Clock JavaDoc для более подробной информации.
Clock.fixed
полезно при тестировании, в то времяClock.system
илиClock.systemUTC
могут быть использованы в приложении.Я использовал новый класс, чтобы скрыть
Clock.fixed
создание и упростить тесты:public class TimeMachine { private static Clock clock = Clock.systemDefaultZone(); private static ZoneId zoneId = ZoneId.systemDefault(); public static LocalDateTime now() { return LocalDateTime.now(getClock()); } public static void useFixedClockAt(LocalDateTime date){ clock = Clock.fixed(date.atZone(zoneId).toInstant(), zoneId); } public static void useSystemDefaultZoneClock(){ clock = Clock.systemDefaultZone(); } private static Clock getClock() { return clock ; } }
public class MyClass { public void doSomethingWithTime() { LocalDateTime now = TimeMachine.now(); ... } }
@Test public void test() { LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2); MyClass myClass = new MyClass(); TimeMachine.useFixedClockAt(twoWeeksAgo); myClass.doSomethingWithTime(); TimeMachine.useSystemDefaultZoneClock(); myClass.doSomethingWithTime(); ... }
источник
getClock()
метод и напрямую использовать поле. Этот метод не добавляет ничего, кроме нескольких строк кода.Я использовал поле
private Clock clock;
а потом
в моем производственном коде. Затем я использовал Mockito в своих модульных тестах, чтобы имитировать часы с помощью Clock.fixed ():
@Mock private Clock clock; private Clock fixedClock;
Издевательство:
Утверждение:
источник
Я считаю,Clock
что ваш производственный код загроможден.Вы можете использовать JMockit или PowerMock для имитации вызовов статических методов в тестовом коде. Пример с JMockit:
@Test public void testSth() { LocalDate today = LocalDate.of(2000, 6, 1); new Expectations(LocalDate.class) {{ LocalDate.now(); result = today; }}; Assert.assertEquals(LocalDate.now(), today); }
РЕДАКТИРОВАТЬ : после прочтения комментариев к ответу Джона Скита на аналогичный вопрос здесь, ТАК, я не согласен со своим прошлым. Больше всего на свете этот аргумент убедил меня в том, что нельзя парализовать тесты, когда вы имитируете статические методы.
Тем не менее, вы можете / должны использовать статические имитации, если вам приходится иметь дело с устаревшим кодом.
источник
Мне нужен
LocalDate
экземпляр вместоLocalDateTime
.По этой причине я создал следующий служебный класс:
public final class Clock { private static long time; private Clock() { } public static void setCurrentDate(LocalDate date) { Clock.time = date.toEpochDay(); } public static LocalDate getCurrentDate() { return LocalDate.ofEpochDay(getDateMillis()); } public static void resetDate() { Clock.time = 0; } private static long getDateMillis() { return (time == 0 ? LocalDate.now().toEpochDay() : time); } }
И его использование похоже на:
class ClockDemo { public static void main(String[] args) { System.out.println(Clock.getCurrentDate()); Clock.setCurrentDate(LocalDate.of(1998, 12, 12)); System.out.println(Clock.getCurrentDate()); Clock.resetDate(); System.out.println(Clock.getCurrentDate()); } }
Вывод:
2019-01-03 1998-12-12 2019-01-03
Заменены все творение ,
LocalDate.now()
чтобыClock.getCurrentDate()
в проекте.Потому что это приложение для весенней загрузки . Перед
test
выполнением профиля просто установите заранее определенную дату для всех тестов:public class TestProfileConfigurer implements ApplicationListener<ApplicationPreparedEvent> { private static final LocalDate TEST_DATE_MOCK = LocalDate.of(...); @Override public void onApplicationEvent(ApplicationPreparedEvent event) { ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment(); if (environment.acceptsProfiles(Profiles.of("test"))) { Clock.setCurrentDate(TEST_DATE_MOCK); } } }
И добавьте в spring.factories :
источник
Вот рабочий способ переопределить текущее системное время на определенную дату для целей тестирования JUnit в веб-приложении Java 8 с EasyMock
Joda Time действительно хорош (спасибо, Стивен, Брайан, вы сделали наш мир лучше), но мне не разрешили его использовать.
После некоторых экспериментов я в конце концов придумал способ имитировать время до определенной даты в Java 8 java.time API с EasyMock.
Вот что нужно сделать:
Что нужно сделать в тестируемом классе
Шаг 1
Добавьте новый
java.time.Clock
атрибут к тестируемому классуMyService
и убедитесь, что новый атрибут будет правильно инициализирован со значениями по умолчанию с блоком создания экземпляра или конструктором:import java.time.Clock; import java.time.LocalDateTime; public class MyService { // (...) private Clock clock; public Clock getClock() { return clock; } public void setClock(Clock newClock) { clock = newClock; } public void initDefaultClock() { setClock( Clock.system( Clock.systemDefaultZone().getZone() // You can just as well use // java.util.TimeZone.getDefault().toZoneId() instead ) ); } { initDefaultClock(); } // initialisation in an instantiation block, but // it can be done in a constructor just as well // (...) }
Шаг 2
Вставьте новый атрибут
clock
в метод, который вызывает текущую дату и время. Например, в моем случае мне пришлось выполнить проверку того, произошла ли ранее дата, хранящаяся в базе данныхLocalDateTime.now()
, которую я заменилLocalDateTime.now(clock)
, например:import java.time.Clock; import java.time.LocalDateTime; public class MyService { // (...) protected void doExecute() { LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB(); while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) { someOtherLogic(); } } // (...) }
Что нужно сделать в тестовом классе
Шаг 3
В тестовом классе создайте объект фиктивных часов и вставьте его в экземпляр тестируемого класса непосредственно перед вызовом тестируемого метода
doExecute()
, а затем сбросьте его обратно сразу после этого, например:import java.time.Clock; import java.time.LocalDateTime; import java.time.OffsetDateTime; import org.junit.Test; public class MyServiceTest { // (...) private int year = 2017; // Be this a specific private int month = 2; // date we need private int day = 3; // to simulate. @Test public void doExecuteTest() throws Exception { // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot MyService myService = new MyService(); Clock mockClock = Clock.fixed( LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()), Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId() ); myService.setClock(mockClock); // set it before calling the tested method myService.doExecute(); // calling tested method myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method // (...) remaining EasyMock stuff: verify(..) and assertEquals(..) } }
Проверьте его в режиме отладки, и вы увидите, что дата 3 февраля 2017 года была правильно введена в
myService
экземпляр и использована в инструкции сравнения, а затем была правильно сброшена на текущую дату с помощьюinitDefaultClock()
.источник
В этом примере даже показано, как объединить Instant и LocalTime ( подробное объяснение проблем с преобразованием )
Тестируемый класс
import java.time.Clock; import java.time.LocalTime; public class TimeMachine { private LocalTime from = LocalTime.MIDNIGHT; private LocalTime until = LocalTime.of(6, 0); private Clock clock = Clock.systemDefaultZone(); public boolean isInInterval() { LocalTime now = LocalTime.now(clock); return now.isAfter(from) && now.isBefore(until); } }
Groovy-тест
import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import java.time.Clock import java.time.Instant import static java.time.ZoneOffset.UTC import static org.junit.runners.Parameterized.Parameters @RunWith(Parameterized) class TimeMachineTest { @Parameters(name = "{0} - {2}") static data() { [ ["01:22:00", true, "in interval"], ["23:59:59", false, "before"], ["06:01:00", false, "after"], ]*.toArray() } String time boolean expected TimeMachineTest(String time, boolean expected, String testName) { this.time = time this.expected = expected } @Test void test() { TimeMachine timeMachine = new TimeMachine() timeMachine.clock = Clock.fixed(Instant.parse("2010-01-01T${time}Z"), UTC) def result = timeMachine.isInInterval() assert result == expected } }
источник
С помощью PowerMockito для теста весенней загрузки вы можете издеваться над
ZonedDateTime
. Вам понадобится следующее.Аннотации
В тестовом классе вам необходимо подготовить сервис, использующий расширение
ZonedDateTime
.@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PrepareForTest({EscalationService.class}) @SpringBootTest public class TestEscalationCases { @Autowired private EscalationService escalationService; //... }
Прецедент
В тесте вы можете подготовить желаемое время и получить его в ответ на вызов метода.
@Test public void escalateOnMondayAt14() throws Exception { ZonedDateTime preparedTime = ZonedDateTime.now(); preparedTime = preparedTime.with(DayOfWeek.MONDAY); preparedTime = preparedTime.withHour(14); PowerMockito.mockStatic(ZonedDateTime.class); PowerMockito.when(ZonedDateTime.now(ArgumentMatchers.any(ZoneId.class))).thenReturn(preparedTime); // ... Assertions }
источник