Tengo una prueba de JUnit que quiero esperar un período de tiempo sincrónicamente. Mi prueba JUnit se ve así:
@Test
public void testExipres(){
SomeCacheObject sco = new SomeCacheObject();
sco.putWithExipration("foo", 1000);
// WAIT FOR 2 SECONDS
assertNull(sco.getIfNotExipred("foo"));
}
Lo intenté Thread.currentThread().wait()
, pero arroja una IllegalMonitorStateException (como se esperaba).
¿Tiene algún truco o necesito un monitor diferente?
java
junit
thread-safety
Kylar
fuente
fuente
Thread.sleep
decir algo como Usar Thread.sleep en una prueba es generalmente una mala idea. Crea pruebas frágiles que pueden fallar de manera impredecible según el entorno ("¡Pasa en mi máquina!") O la carga. No confíe en el tiempo (use simulacros) ni use bibliotecas como Awaitility para pruebas asincrónicas.Thread.sleep () podría funcionar en la mayoría de los casos, pero generalmente si está esperando, en realidad está esperando que ocurra una condición o estado en particular. Thread.sleep () no garantiza que lo que esté esperando haya sucedido realmente.
Si está esperando una solicitud de descanso, por ejemplo, tal vez generalmente regrese en 5 segundos, pero si configura su sueño durante 5 segundos el día en que su solicitud regresa en 10 segundos, su prueba fallará.
Para remediar esto, JayWay tiene una gran utilidad llamada Awatility que es perfecta para asegurar que ocurra una condición específica antes de seguir adelante.
También tiene una buena API fluida
await().until(() -> { return yourConditionIsMet(); });
https://github.com/jayway/awaitility
fuente
En caso de que su analizador de código estático (como SonarQube) se queje, pero no puede pensar en otra forma, en lugar de dormir, puede intentar con un truco como:
Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true);
Es conceptualmente incorrecto, pero es lo mismo queThread.sleep(1000)
.La mejor manera, por supuesto, es aprobar un Callable, con su condición apropiada, en lugar de la
true
que tengo.https://github.com/awaitility/awaitility
fuente
Duration
clase, pero desde la versión 4 le cambiaron el nombre aDurations
.Puede usar la biblioteca java.util.concurrent.TimeUnit que internamente usa Thread.sleep. La sintaxis debería verse así:
@Test public void testExipres(){ SomeCacheObject sco = new SomeCacheObject(); sco.putWithExipration("foo", 1000); TimeUnit.MINUTES.sleep(2); assertNull(sco.getIfNotExipred("foo")); }
Esta biblioteca proporciona una interpretación más clara para la unidad de tiempo. Puede usar 'HORAS' / 'MINUTOS' / 'SEGUNDOS'.
fuente
sleep()
afectará el hilo de trabajo?También puede utilizar el
CountDownLatch
objeto como se explica aquí .fuente
Hay un problema general: es difícil burlarse del tiempo. Además, es una mala práctica colocar un código de larga ejecución / espera en una prueba unitaria.
Entonces, para hacer que una API de programación se pueda probar, utilicé una interfaz con una implementación real y una simulación como esta:
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); } }
Con esto, podrías imitar el tiempo en tu prueba:
@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")); }
Un simulacro avanzado de subprocesos múltiples
Clock
es mucho más complejo, por supuesto, pero puedes hacerlo conThreadLocal
referencias y una buena estrategia de sincronización de tiempo, por ejemplo.fuente
Si es absolutamente necesario generar retraso en una prueba
CountDownLatch
es una solución sencilla. En su clase de prueba declare:private final CountDownLatch waiter = new CountDownLatch(1);
y en la prueba donde sea necesario:
waiter.await(1000 * 1000, TimeUnit.NANOSECONDS); // 1ms
Tal vez sea innecesario decirlo, pero tenga en cuenta que debe mantener los tiempos de espera pequeños y no acumular esperas en demasiados lugares.
fuente