Systeemtijd overschrijven voor testen in Java

1. Overzicht

In deze korte tutorial zullen we ons concentreren op verschillende manieren om de systeemtijd voor testen op te heffen.

Soms zit er een logica rond de huidige datum in onze code. Misschien enkele functie-oproepen zoals nieuwe datum() of Calendar.getInstance (), die uiteindelijk gaan bellen System.CurrentTimeMillis.

Voor een inleiding in het gebruik van Java-klok, verwijzen wij u naar dit artikel hier. Of, op het gebruik van AspectJ, hier.

2. Inklokken gebruiken java.time

De java.time pakket in Java 8 bevat een abstracte klasse java.time.Clock met het doel om alternatieve klokken aan te sluiten wanneer dat nodig is. Daarmee kunnen we onze eigen implementatie aansluiten of er een vinden die al is gemaakt om aan onze behoeften te voldoen.

Om onze doelen te bereiken, de bovenstaande bibliotheek bevat statische methoden om speciale implementaties op te leveren. We gaan er twee gebruiken die een onveranderlijke, thread-veilige en serialiseerbare implementatie retourneren.

De eerste is gemaakt. Van daaruit we kunnen een Klok dat geeft altijd hetzelfde terug Onmiddellijk, ervoor zorgen dat de tests niet afhankelijk zijn van de huidige klok.

Om het te gebruiken, hebben we een Onmiddellijk en een ZoneOffset:

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

De tweede statische methode is compensatie. In deze omhult een klok een andere klok waardoor het het geretourneerde object is dat instants kan krijgen die later of eerder zijn met de opgegeven duur.

Met andere woorden, het is mogelijk om hardlopen in de toekomst, in het verleden of op een willekeurig moment in de tijd te simuleren:

Clock constantClock = Clock.fixed (ofEpochMilli (0), ZoneId.systemDefault ()); // ga naar de toekomst: Clock clock = Clock.offset (constantClock, Duration.ofSeconds (10)); // terugspoelen met een negatieve waarde: clock = Clock.offset (constantClock, Duration.ofSeconds (-5)); // de duur van 0 keert terug naar dezelfde klok: clock = Clock.offset (constClock, Duration.ZERO);

Met de Looptijd klasse, is het mogelijk om te manipuleren van nanoseconden tot dagen. We kunnen ook een duur ontkennen, wat betekent dat we een kopie van deze duur krijgen met de ontkende lengte.

3. Aspect-georiënteerd programmeren gebruiken

Een andere manier om de systeemtijd op te heffen, is door AOP. Met deze aanpak we zijn in staat om de Systeem class om een ​​vooraf gedefinieerde waarde te retourneren die we in onze testcases kunnen instellen.

Het is ook mogelijk om de toepassingsklassen te weven om de oproep naar om te leiden System.currentTimeMillis () of te nieuwe datum() naar een andere eigen klasse van nutsbedrijven.

Een manier om dit te implementeren is door het gebruik van AspectJ:

openbaar aspect ChangeCallsToCurrentTimeInMillisMethod {long around (): call (publieke statische native lange java.lang.System.currentTimeMillis ()) && binnen (user.code.base.pckg. *) {return 0; }} 

In het bovenstaande voorbeeld we vangen elke oproep op System.currentTimeMillis() in een gespecificeerd pakket, wat in dit geval is gebruiker.code.base.pckg. *, en elke keer dat deze gebeurtenis plaatsvindt, nul retourneren.

Het is op deze plek waar we onze eigen implementatie kunnen declareren om de gewenste tijd in milliseconden te verkrijgen.

Een voordeel van het gebruik van AspectJ is dat het rechtstreeks op bytecodeniveau werkt, zodat het niet de originele broncode nodig heeft om te werken.

Om die reden hoeven we het niet opnieuw te compileren.

4. Bespotten van de Instant.now () Methode

We kunnen de Onmiddellijk klasse om een ​​momentane punt op de tijdlijn weer te geven. Normaal gesproken kunnen we het gebruiken om tijdstempels van gebeurtenissen in onze applicatie op te nemen. De nu() methode van deze klasse stelt ons in staat om het huidige moment uit de systeemklok in de UTC-tijdzone te halen.

Laten we eens kijken naar enkele alternatieven om zijn gedrag te veranderen tijdens het testen.

4.1. Overbelasting nu() Met een Klok

We kunnen het nu() methode met een vaste Klok voorbeeld. Veel van de klassen in de java.time pakket hebben een nu() methode waarvoor een Klok parameter, waardoor dit onze voorkeursaanpak is:

@Test openbare leegte gegevenFixedClock_whenNow_thenGetFixedInstant () {String instantExpected = "2014-12-22T10: 15: 30Z"; Clock clock = Clock.fixed (Instant.parse (instantExpected), ZoneId.of ("UTC")); Instant instant = Instant.now (klok); assertThat (instant.toString ()). isEqualTo (instantExpected); }

4.2. Gebruik makend van PowerMock

Als we bovendien het gedrag van het nu() methode zonder parameters te verzenden, kunnen we gebruiken PowerMock:

@RunWith (PowerMockRunner.class) @PrepareForTest ({Instant.class}) openbare klasse InstantUnitTest {@Test openbare leegte gegevenInstantMock_whenNow_thenGetFixedInstant () {String instantExpected = "2014-12-22T10: 15: 30Z"; Clock clock = Clock.fixed (Instant.parse (instantExpected), ZoneId.of ("UTC")); Instant instant = Instant.now (klok); mockStatic (Instant.class); when (Instant.now ()). thenReturn (instant); Instant now = Instant.now (); assertThat (now.toString ()). isEqualTo (instantExpected); }}

4.3. Gebruik makend van JMockit

Als alternatief kunnen we de JMockit bibliotheek.

JMockit biedt ons twee manieren om een ​​statische methode te bespotten. Een daarvan is het gebruik van de MockUp klasse:

@Test openbare leegte gegevenInstantWithJMock_whenNow_thenGetFixedInstant () {String instantExpected = "2014-12-21T10: 15: 30Z"; Clock clock = Clock.fixed (Instant.parse (instantExpected), ZoneId.of ("UTC")); nieuwe MockUp () {@Mock public Instant now () {return Instant.now (klok); }}; Instant now = Instant.now (); assertThat (now.toString ()). isEqualTo (instantExpected); }

En de andere gebruikt de Verwachtingen klasse:

@Test openbare leegte gegevenInstantWithExpectations_whenNow_thenGetFixedInstant () {Clock clock = Clock.fixed (Instant.parse ("2014-12-23T10: 15: 30.00Z"), ZoneId.of ("UTC")); Instant instantExpected = Instant.now (klok); nieuwe verwachtingen (Instant.class) {{Instant.now (); result = instantExpected; }}; Instant now = Instant.now (); assertThat (nu) .isEqualTo (instantExpected); }

5. Bespotten van de LocalDateTime.now () Methode

Een andere nuttige klasse in de java.time pakket is het LocalDateTime klasse. Deze klasse vertegenwoordigt een datum-tijd zonder tijdzone in het ISO-8601 kalendersysteem. De nu() methode van deze klasse stelt ons in staat om de huidige datum-tijd uit de systeemklok in de standaard tijdzone te halen.

We kunnen dezelfde alternatieven gebruiken om het te bespotten zoals we eerder zagen. Bijvoorbeeld overbelasting nu() met een vaste Klok:

@Test openbare ongeldig gegevenFixedClock_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 (klok); assertThat (dateTime) .isEqualTo (dateTimeExpected); }

6. Conclusie

In dit artikel hebben we verschillende manieren onderzocht om de systeemtijd voor testen te overschrijven. We hebben eerst gekeken naar het native pakket java.time en zijn Klok klasse. Vervolgens hebben we gezien hoe we een aspect kunnen toepassen om de Systeem klasse. Ten slotte zagen we verschillende alternatieven voor het bespotten van de nu() methode op Onmiddellijk en LocalDateTime klassen.

Zoals altijd zijn codevoorbeelden te vinden op GitHub.