Overstyring av systemtid for testing i Java

1. Oversikt

I denne raske opplæringen vil vi fokusere på forskjellige måter å overstyre systemtiden for testing.

Noen ganger er det en logikk rundt dagens dato i koden vår. Kanskje noen funksjonsanrop som ny dato () eller Calendar.getInstance (), som til slutt kommer til å ringe System.CurrentTimeMillis.

For en introduksjon til bruken av Java-klokke, se denne artikkelen her. Eller til bruk av AspectJ, her.

2. Bruke Clock in java.time

De java.time pakke inn Java 8 inkluderer en abstrakt klasse java.time.Clock med det formål å tillate at alternative klokker kobles til etter behov. Med det kan vi koble til vår egen implementering eller finne en som allerede er laget for å tilfredsstille våre behov.

For å nå våre mål, biblioteket ovenfor inkluderer statiske metoder for å gi spesielle implementeringer. Vi kommer til å bruke to av dem som gir en uforanderlig, trådsikker og seriell implementering.

Den første er fikset. Fra det, vi kan få en Klokke som alltid returnerer det samme Umiddelbar, sørge for at testene ikke er avhengige av gjeldende klokke.

For å bruke den trenger vi en Umiddelbar og en ZoneOffset:

Instant.now (Clock.fixed (Instant.parse ("2018-08-22T10: 00: 00Z"), ZoneOffset.UTC))

Den andre statiske metoden er forskyvning. I denne bryter en klokke en annen klokke som gjør det til det returnerte objektet som er i stand til å få øyeblikk som er senere eller tidligere med den angitte varigheten.

Med andre ord, det er mulig å simulere løping i fremtiden, i det siste eller på et hvilket som helst vilkårlig tidspunkt:

Clock constantClock = Clock.fixed (ofEpochMilli (0), ZoneId.systemDefault ()); // gå til fremtiden: Clock Clock = Clock.offset (constantClock, Duration.ofSeconds (10)); // spole tilbake med en negativ verdi: klokke = Clock.offset (constantClock, Duration.ofSeconds (-5)); // varigheten 0 returnerer til samme klokke: klokke = Clock.offset (constClock, Duration.ZERO);

Med Varighet klasse, er det mulig å manipulere fra nanosekunder til dager. Vi kan også negere en varighet, noe som betyr å få en kopi av denne varigheten med lengden negert.

3. Bruke aspektorientert programmering

En annen måte å overstyre systemtiden på er AOP. Med denne tilnærmingen, vi er i stand til å veve System klasse for å returnere en forhåndsdefinert verdi som vi kan sette i testsakene våre.

Det er også mulig å veve applikasjonsklassene for å omdirigere samtalen til System.currentTimeMillis () eller til ny dato () til en annen egen nytteklasse.

En måte å implementere dette på er gjennom bruk av AspectJ:

offentlig aspekt ChangeCallsToCurrentTimeInMillisMethod {long around (): call (public static native long java.lang.System.currentTimeMillis ()) && within (user.code.base.pckg. *) {return 0; }} 

I eksemplet ovenfor, vi tar hver samtale til System.currentTimeMillis() inne i en spesifisert pakke, som i dette tilfellet er bruker.kode.base.pckg. *, og returnerer null hver gang denne hendelsen skjer.

Det er på dette stedet hvor vi kan erklære vår egen implementering for å oppnå ønsket tid i millisekunder.

En fordel med å bruke AspectJ er at den opererer på bytecode-nivå direkte, slik at den ikke trenger den originale kildekoden for å fungere.

Av den grunn trenger vi ikke å kompilere den på nytt.

4. Hånende Øyeblikkelig. Nå () Metode

Vi kan bruke Umiddelbar klasse for å representere et øyeblikkelig punkt på tidslinjen. Normalt kan vi bruke den til å registrere tidsstempler for hendelser i applikasjonen vår. De nå() metoden i denne klassen lar oss få dagens øyeblikk fra systemklokken i UTC-tidssone.

La oss se noen alternativer for å endre oppførselen når vi tester.

4.1. Overbelastning nå() Med en Klokke

Vi kan overbelaste nå() metode med en fast Klokke forekomst. Mange av klassene i java.time pakken har en nå() metode som tar en Klokke parameter, som gjør dette til vår foretrukne tilnærming:

@Test offentlig ugyldig givenFixedClock_whenNow_thenGetFixedInstant () {String instantExpected = "2014-12-22T10: 15: 30Z"; Clock Clock = Clock.fixed (Instant.parse (instantExpected), ZoneId.of ("UTC")); Øyeblikkelig øyeblikkelig = Øyeblikkelig. Nå (klokke); assertThat (instant.toString ()). erEqualTo (instantExpected); }

4.2. Ved hjelp av PowerMock

I tillegg, hvis vi trenger å endre atferden til nå() metode uten å sende parametere, kan vi bruke PowerMock:

@RunWith (PowerMockRunner.class) @PrepareForTest ({Instant.class}) offentlig klasse InstantUnitTest {@Test offentlig ugyldig givenInstantMock_whenNow_thenGetFixedInstant () {String instantExpected = "2014-12-22T10: 15: 30Z"; Clock Clock = Clock.fixed (Instant.parse (instantExpected), ZoneId.of ("UTC")); Øyeblikkelig øyeblikkelig = Øyeblikkelig. Nå (klokke); mockStatic (Instant.class); når (Instant.now ()). thenReturn (instant); Øyeblikkelig nå = Øyeblikkelig. Nå (); assertThat (now.toString ()). er EqualTo (instantExpected); }}

4.3. Ved hjelp av JMockit

Alternativt kan vi bruke JMockit bibliotek.

JMockit tilbyr oss to måter å spotte en statisk metode på. Den ene bruker MockUp klasse:

@Test offentlig ugyldighet givenInstantWithJMock_whenNow_thenGetFixedInstant () {String instantExpected = "2014-12-21T10: 15: 30Z"; Clock Clock = Clock.fixed (Instant.parse (instantExpected), ZoneId.of ("UTC")); ny MockUp () {@Mock offentlig øyeblikkelig nå () {returner øyeblikkelig. nå (klokke); }}; Øyeblikkelig nå = Øyeblikkelig. Nå (); assertThat (now.toString ()). erEqualTo (instantExpected); }

Og den andre bruker Forventninger klasse:

@Test offentlig ugyldig givenInstantWithExpectations_whenNow_thenGetFixedInstant () {Clock Clock = Clock.fixed (Instant.parse ("2014-12-23T10: 15: 30.00Z"), ZoneId.of ("UTC")); Instant instantExpected = Instant.now (klokke); nye forventninger (Instant.class) {{Instant.now (); resultat = instantExpected; }}; Øyeblikkelig nå = Øyeblikkelig. Nå (); assertThat (nå) .isEqualTo (instantExpected); }

5. Hånende LocalDateTime.now () Metode

En annen nyttig klasse i java.time pakken er LocalDateTime klasse. Denne klassen representerer en dato og tid uten tidssone i ISO-8601 kalendersystemet. De nå() metoden i denne klassen lar oss hente gjeldende dato fra systemklokken i standard tidssone.

Vi kan bruke de samme alternativene til å spotte det som vi så før. For eksempel overbelastning nå() med en fast Klokke:

@Test offentlig ugyldig givenFixedClock_whenNow_thenGetFixedLocalDateTime () {Clock Clock = Clock.fixed (Instant.parse ("2014-12-22T10: 15: 30.00Z"), ZoneId.of ("UTC")); String dateTimeExpected = "2014-12-22T10: 15: 30"; LocalDateTime dateTime = LocalDateTime.now (klokke); assertThat (dateTime) .isEqualTo (dateTimeExpected); }

6. Konklusjon

I denne artikkelen har vi utforsket forskjellige måter å overstyre systemtiden for testing. Først så vi på den opprinnelige pakken java.time og dets Klokke klasse. Deretter så vi hvordan vi kunne bruke et aspekt for å veve System klasse. Til slutt så vi forskjellige alternativer til å spotte nå() metode på Umiddelbar og LocalDateTime klasser.

Som alltid kan kodeeksempler bli funnet på GitHub.

$config[zx-auto] not found$config[zx-overlay] not found