J'ai un test JUnit que je veux faire attendre pendant un certain temps, de manière synchrone. Mon test JUnit ressemble à ceci:
@Test
public void testExipres(){
SomeCacheObject sco = new SomeCacheObject();
sco.putWithExipration("foo", 1000);
//WAIT FOR 2 SECONDS
assertNull(sco.getIfNotExipred("foo"));
}
J'ai essayé Thread.currentThread (). Wait (), mais il jette une exception IllegalMonitorStateException (comme prévu). Y at-il une astuce ou ai-je besoin d'un autre moniteur?
Que diriez-vous de Thread.sleep(2000);
? :)
Thread.sleep () pourrait fonctionner dans la plupart des cas, mais généralement si vous attendez, vous attendez réellement qu'une condition ou un état particulier se produise. Thread.sleep () ne garantit pas que tout ce que vous attendez a bien eu lieu.
Si vous attendez une demande de repos, par exemple, elle revient généralement dans les 5 secondes, mais si vous réglez votre sommeil sur 5 secondes le jour où votre demande revient dans les 10 secondes, votre test échouera.
Pour remédier à cela, JayWay a un grand utilitaire appelé Awatility , qui est parfait pour s’assurer qu’une condition particulière survient avant de continuer.
Il a une belle api couramment aussi bien
await().until(() ->
{
return yourConditionIsMet();
});
Si votre analyseur de code statique (comme SonarQube) se plaint, mais que vous ne pouvez pas penser à une autre façon, plutôt que de dormir, vous pouvez essayer avec un hack comme: Awaitility.await().pollDelay(Duration.ONE_SECOND).until(() -> true);
C'est conceptuellement incorrect, mais c'est la même chose Thread.sleep(1000)
.
La meilleure façon, bien sûr, est de passer un Callable, avec votre condition appropriée, plutôt que true
, que j'ai.
Vous pouvez également utiliser l'objet CountDownLatch
comme expliqué ici .
Vous pouvez utiliser la bibliothèque Java.util.concurrent.TimeUnit qui utilise en interne Thread.sleep. La syntaxe devrait ressembler à ceci:
@Test
public void testExipres(){
SomeCacheObject sco = new SomeCacheObject();
sco.putWithExipration("foo", 1000);
TimeUnit.MINUTES.sleep(2);
assertNull(sco.getIfNotExipred("foo"));
}
Cette bibliothèque fournit une interprétation plus claire pour l'unité de temps. Vous pouvez utiliser 'HEURES'/'MINUTES'/'SECONDES'.
Il y a un problème général: il est difficile de se moquer du temps. En outre, il est vraiment déconseillé de placer un code long en attente/en attente dans un test unitaire.
Donc, pour rendre une API de planification testable, j'ai utilisé une interface avec une implémentation réelle et fictive comme celle-ci:
public interface Clock {
public long getCurrentMillis();
public void sleep(long millis) throws InterruptedException;
}
public static class SystemClock implements Clock {
@Override
public long getCurrentMillis() {
return System.currentTimeMillis();
}
@Override
public void sleep(long millis) throws InterruptedException {
Thread.sleep(millis);
}
}
public static class MockClock implements Clock {
private final AtomicLong currentTime = new AtomicLong(0);
public MockClock() {
this(System.currentTimeMillis());
}
public MockClock(long currentTime) {
this.currentTime.set(currentTime);
}
@Override
public long getCurrentMillis() {
return currentTime.addAndGet(5);
}
@Override
public void sleep(long millis) {
currentTime.addAndGet(millis);
}
}
Avec cela, vous pouvez imiter le temps dans votre test:
@Test
public void testExipres() {
MockClock clock = new MockClock();
SomeCacheObject sco = new SomeCacheObject();
sco.putWithExipration("foo", 1000);
clock.sleep(2000) // WAIT FOR 2 SECONDS
assertNull(sco.getIfNotExpired("foo"));
}
Une maquette multi-threading avancée pour Clock
est beaucoup plus complexe, bien sûr, mais vous pouvez le créer avec les références ThreadLocal
et une bonne stratégie de synchronisation temporelle, par exemple.