Joda Time ma ładny DateTimeUtils.setCurrentMillisFixed () do pozorowania czasu.
Jest to bardzo praktyczne w testach.
Czy istnieje odpowiednik w java.time API Java 8 ?
Joda Time ma ładny DateTimeUtils.setCurrentMillisFixed () do pozorowania czasu.
Jest to bardzo praktyczne w testach.
Czy istnieje odpowiednik w java.time API Java 8 ?
Najbliższą rzeczą jest Clock
przedmiot. Możesz utworzyć obiekt Clock w dowolnym momencie (lub z bieżącego czasu Systemu). Wszystkie obiekty date.time mają przeciążone now
metody, które pobierają obiekt zegara zamiast aktualnego czasu. Więc możesz użyć iniekcji zależności, aby wstrzyknąć zegar z określonym czasem:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
Aby uzyskać więcej informacji, zobacz Clock JavaDoc
Clock.fixed
jest przydatny w testowaniu, podczas gdyClock.system
lubClock.systemUTC
może być używany w aplikacji.Użyłem nowej klasy, aby ukryć
Clock.fixed
tworzenie i uprościć testy: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(); ... }
źródło
getClock()
metodę i bezpośrednio użyć pola. Ta metoda dodaje tylko kilka wierszy kodu.Użyłem pola
private Clock clock;
i wtedy
w moim kodzie produkcyjnym. Następnie użyłem Mockito w moich testach jednostkowych, aby mockować Zegar za pomocą Clock.fixed ():
@Mock private Clock clock; private Clock fixedClock;
Kpiny:
Twierdzenie:
źródło
Uważam, żeClock
zaśmiecam twój kod produkcyjny.Można użyć JMockit lub PowerMock mock statycznej metody wywołania w kodzie testowym. Przykład z 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); }
EDYCJA : Po przeczytaniu komentarzy do odpowiedzi Jona Skeeta na podobne pytanie tutaj na SO, nie zgadzam się z moim przeszłym sobą. Bardziej niż cokolwiek innego argument ten przekonał mnie, że nie można paralizować testów, gdy kpisz z metod statycznych.
Jednak możesz / musisz nadal używać statycznego mockowania, jeśli masz do czynienia ze starszym kodem.
źródło
Potrzebuję
LocalDate
instancji zamiastLocalDateTime
.Z tego powodu stworzyłem następującą klasę użytkową:
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); } }
A użycie do tego jest następujące:
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()); } }
Wynik:
2019-01-03 1998-12-12 2019-01-03
Zastąpione całe stworzenie
LocalDate.now()
, abyClock.getCurrentDate()
w projekcie.Ponieważ jest to aplikacja do rozruchu sprężynowego . Przed
test
wykonaniem profilu wystarczy ustawić predefiniowaną datę dla wszystkich testów: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); } } }
I dodaj do wiosny. Fabryki :
źródło
Oto działający sposób na zastąpienie bieżącego czasu systemowego do określonej daty do celów testowania JUnit w aplikacji internetowej Java 8 za pomocą EasyMock
Joda Time jest z pewnością fajny (dziękuję Stephen, Brian, uczyniłeś nasz świat lepszym miejscem), ale nie pozwolono mi go używać.
Po kilku eksperymentach w końcu wymyśliłem sposób na udawanie czasu do określonej daty w java.time API Java 8 za pomocą EasyMock
Oto, co należy zrobić:
Co należy zrobić w testowanej klasie
Krok 1
Dodaj nowy
java.time.Clock
atrybut do testowanej klasyMyService
i upewnij się, że nowy atrybut zostanie poprawnie zainicjowany z wartościami domyślnymi za pomocą bloku instancji lub konstruktora: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 // (...) }
Krok 2
Wprowadź nowy atrybut
clock
do metody, która wywołuje bieżącą datę-czas. Na przykład w moim przypadku musiałem sprawdzić, czy data zapisana w bazie danych wydarzyła się wcześniejLocalDateTime.now()
, którą zamieniłem naLocalDateTime.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(); } } // (...) }
Co należy zrobić w klasie testowej
Krok 3
W klasie testowej utwórz pozorowany obiekt zegara i wstrzyknij go do instancji testowanej klasy tuż przed wywołaniem testowanej metody
doExecute()
, a następnie zresetuj ją z powrotem, na przykład: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(..) } }
Sprawdź to w trybie debugowania, a zobaczysz, że data 3 lutego 2017 została poprawnie wstrzyknięta do
myService
instancji i użyta w instrukcji porównawczej, a następnie została poprawnie zresetowana do aktualnej daty zinitDefaultClock()
.źródło
Ten przykład pokazuje nawet, jak połączyć Instant i LocalTime ( szczegółowe wyjaśnienie problemów z konwersją )
Testowana klasa
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); } }
Test Groovy'ego
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 } }
źródło
Z pomocą PowerMockito do testu sprężynowego rozruchu możesz kpić z
ZonedDateTime
. Potrzebujesz następujących elementów.Adnotacje
Na zajęciach testowych musisz przygotować usługę korzystającą z
ZonedDateTime
.@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PrepareForTest({EscalationService.class}) @SpringBootTest public class TestEscalationCases { @Autowired private EscalationService escalationService; //... }
Przypadek testowy
W teście możesz przygotować żądany czas i uzyskać go w odpowiedzi na wywołanie metody.
@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 }
źródło