Joda Time a un joli DateTimeUtils.setCurrentMillisFixed () pour simuler l'heure.
C'est très pratique dans les tests.
Existe-t-il un équivalent dans l'API java.time de Java 8 ?
Joda Time a un joli DateTimeUtils.setCurrentMillisFixed () pour simuler l'heure.
C'est très pratique dans les tests.
Existe-t-il un équivalent dans l'API java.time de Java 8 ?
La chose la plus proche est l' Clock
objet. Vous pouvez créer un objet Horloge à tout moment (ou à partir de l'heure actuelle du système). Tous les objets date.time ont des now
méthodes surchargées qui prennent un objet horloge à la place pour l'heure actuelle. Vous pouvez donc utiliser l'injection de dépendances pour injecter une horloge avec une heure spécifique:
public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}
Voir Clock JavaDoc pour plus de détails
Clock.fixed
est utile pour les tests, pendantClock.system
ouClock.systemUTC
pourrait être utilisé dans l'application.J'ai utilisé une nouvelle classe pour masquer la
Clock.fixed
création et simplifier les tests: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(); ... }
la source
getClock()
méthode et utiliser le champ directement. Cette méthode n'ajoute rien d'autre que quelques lignes de code.J'ai utilisé un champ
private Clock clock;
puis
dans mon code de production. Ensuite, j'ai utilisé Mockito dans mes tests unitaires pour simuler l'horloge en utilisant Clock.fixed ():
@Mock private Clock clock; private Clock fixedClock;
Railleur:
Affirmation:
la source
Je trouve que l'utilisationClock
encombre votre code de production.Vous pouvez utiliser JMockit ou PowerMock pour simuler les appels de méthode statique dans votre code de test. Exemple avec 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 : Après avoir lu les commentaires sur la réponse de Jon Skeet à une question similaire ici sur SO, je ne suis pas d'accord avec mon passé. Plus que toute autre chose, l'argument m'a convaincu que vous ne pouvez pas paralléliser les tests lorsque vous vous moquez de méthodes statiques.
Cependant, vous pouvez / devez toujours utiliser la simulation statique si vous devez gérer du code hérité.
la source
J'ai besoin d'une
LocalDate
instance au lieu deLocalDateTime
.Pour cette raison, j'ai créé la classe utilitaire suivante:
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); } }
Et son utilisation est comme:
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()); } }
Production:
2019-01-03 1998-12-12 2019-01-03
Remplacée toute la création
LocalDate.now()
àClock.getCurrentDate()
dans le projet.Parce que c'est une application de démarrage à ressort . Avant l'
test
exécution du profil, définissez simplement une date prédéfinie pour tous les tests: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); } } }
Et ajoutez à spring.factories :
la source
Voici une méthode de travail pour remplacer l'heure système actuelle par une date spécifique à des fins de test JUnit dans une application Web Java 8 avec EasyMock
Joda Time est bien sûr gentil (merci Stephen, Brian, vous avez rendu notre monde meilleur) mais je n'ai pas été autorisé à l'utiliser.
Après quelques expérimentations, j'ai finalement trouvé un moyen de simuler l'heure à une date spécifique dans l'API java.time de Java 8 avec EasyMock
Voici ce qu'il faut faire:
Que faut-il faire dans la classe testée
Étape 1
Ajoutez un nouvel
java.time.Clock
attribut à la classe testéeMyService
et assurez-vous que le nouvel attribut sera correctement initialisé aux valeurs par défaut avec un bloc d'instanciation ou un constructeur: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 // (...) }
Étape 2
Injectez le nouvel attribut
clock
dans la méthode qui appelle une date-heure actuelle. Par exemple, dans mon cas, j'ai dû vérifier si une date stockée dans la base de données était antérieureLocalDateTime.now()
, que j'ai remplacée parLocalDateTime.now(clock)
, comme ceci: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(); } } // (...) }
Que faut-il faire dans la classe de test
Étape 3
Dans la classe de test, créez un objet d'horloge fictif et injectez-le dans l'instance de la classe testée juste avant d'appeler la méthode testée
doExecute()
, puis réinitialisez-le juste après, comme ceci: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(..) } }
Vérifiez-le en mode débogage et vous verrez que la date du 3 février 2017 a été correctement injectée dans l'
myService
instance et utilisée dans l'instruction de comparaison, puis a été correctement réinitialisée à la date actuelle avecinitDefaultClock()
.la source
Cet exemple montre même comment combiner Instant et LocalTime ( explication détaillée des problèmes de conversion )
Une classe sous test
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); } }
Un test 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 } }
la source
Avec l'aide de PowerMockito pour un test de démarrage à ressort, vous pouvez vous moquer du
ZonedDateTime
. Vous avez besoin des éléments suivants.Annotations
Sur la classe de test, vous devez préparer le service qui utilise le
ZonedDateTime
.@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PrepareForTest({EscalationService.class}) @SpringBootTest public class TestEscalationCases { @Autowired private EscalationService escalationService; //... }
Cas de test
Dans le test, vous pouvez préparer l'heure souhaitée et l'obtenir en réponse à l'appel de méthode.
@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 }
la source