Насмешливое время в Java 8 Java для.время API
Джода время имеет хороший DateTimeUtils.setCurrentMillisFixed () издеваться над временем. Это очень практично в тестах. Есть ли эквивалент в в Java 8 Java для.время API?
6 ответов:
я использовал новый класс, чтобы скрыть
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(); ... }
использование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); }
EDIT: после прочтения комментариев к ответу Джона Скита на аналогичный вопрос вот так Я не согласен с моим прошлым. Больше всего аргумент убедил меня, что вы не можете распараллелить тесты когда вы издеваетесь над статическими методами.
вы можете / должны по-прежнему использовать статическое издевательство, если вам приходится иметь дело с устаревшим кодом.
я использовал поле
private Clock clock;
а то
LocalDate.now(clock);
в моем производстве код. Затем я использовал Mockito в своих модульных тестах, чтобы издеваться над часами с помощью часов.исправлено ():
@Mock private Clock clock; private Clock fixedClock;
насмешливый:
fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); doReturn(fixedClock.instant()).when(clock).instant(); doReturn(fixedClock.getZone()).when(clock).getZone();
утверждение:
assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock)));
вот рабочий способ переопределить текущее системное время на определенную дату для целей тестирования JUnit в веб-приложении Java 8 С помощью EasyMock
время Джоды, конечно, приятно (спасибо Стивен, Брайан, вы сделали наш мир лучше), но мне не разрешили его использовать.
после некоторых экспериментов я в конце концов придумал способ издеваться над временем до определенной даты в java 8-х java.время API с EasyMock
- без времени Джоды API
- и без PowerMock.
вот что нужно сделать:
что нужно сделать в тестируемом классе
Шаг 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
в метод что требует текущей даты-времени. Например, в моем случае мне пришлось выполнить проверку того, произошла ли дата, хранящаяся в dataase, раньше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
в тестовом классе создайте макет объекта clock и введите его в экземпляр тестируемого класса непосредственно перед вызовом тестируемого метода
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(..) } }
проверьте его в режиме отладки, и вы увидите, что дата 2017 Feb 3 была правильно введена в
myService
экземпляр и используется в инструкции сравнения, а затем был правильно сброшен на текущую дату сinitDefaultClock()
.
этот пример даже показывает, как объединить мгновенное и локальное время (подробное объяснение проблем с преобразованием)
тестируемый класс
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); } }
отличный тест
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 } }