¿Cómo puedo hacer esperar una prueba de JUnit?

95

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?

Kylar
fuente

Respuestas:

119

¿Qué tal Thread.sleep(2000);? :)

Muel
fuente
15
Si está utilizando herramientas de análisis de código como sonarqube, se quejarán de Thread.sleepdecir 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.
FuryFart
4
Esta respuesta debe eliminarse y considerarse perjudicial. Una respuesta mucho mejor a continuación es stackoverflow.com/a/35163873/1229735
yiati
73

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

Ben Glasser
fuente
No pude obtener la capacidad de espera para compilar en Android Studio.
IgorGanapolsky
¿Agregó esta línea a su archivo gradle: compile 'org.awaitility: awaitility: 3.0.0'?
Samoht
No. agregaría esto en un caso de prueba en el que desea esperar una condición particular
Ben Glasser
16

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 que Thread.sleep(1000).

La mejor manera, por supuesto, es aprobar un Callable, con su condición apropiada, en lugar de la trueque tengo.

https://github.com/awaitility/awaitility

rastaman
fuente
@Jitendra: Tenga en cuenta que Awaitility solía tener la Durationclase, pero desde la versión 4 le cambiaron el nombre a Durations.
Jacob van Lingen
Una advertencia, si desea que la demora de la encuesta sea de 10 segundos o más, asegúrese de aumentar el tiempo de espera a más de 10 segundos, ya que es el tiempo de espera predeterminado y awaitility se quejará de que el tiempo de espera es menor que la demora de la encuesta.
enfoque
15

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'.

Akshay Pagar
fuente
Si inicio un hilo de trabajo desde dentro de una prueba, ¿ sleep()afectará el hilo de trabajo?
Anthony Kong
6

También puede utilizar el CountDownLatchobjeto como se explica aquí .

Pierre Voisin
fuente
0

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 Clockes mucho más complejo, por supuesto, pero puedes hacerlo con ThreadLocalreferencias y una buena estrategia de sincronización de tiempo, por ejemplo.

Dávid Horváth
fuente
0

Si es absolutamente necesario generar retraso en una prueba CountDownLatches 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.

pirho
fuente