Come posso far aspettare un test JUnit?


93

Ho un test JUnit che voglio aspettare per un periodo di tempo, in modo sincrono. Il mio test JUnit ha questo aspetto:

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);
    //WAIT FOR 2 SECONDS
    assertNull(sco.getIfNotExipred("foo"));
}

Ho provato Thread.currentThread (). Wait (), ma genera un'eccezione IllegalMonitorStateException (come previsto). C'è qualche trucco o ho bisogno di un monitor diverso?

Risposte:


117

Che ne dici Thread.sleep(2000);? :)


14
Se stai usando strumenti di analisi del codice come sonarqube, si lamenteranno di Thread.sleepdire qualcosa come Usare Thread.sleep in un test è solo generalmente una cattiva idea. Crea test fragili che possono fallire in modo imprevedibile a seconda dell'ambiente ("Passa sulla mia macchina!") O del carico. Non fare affidamento sul tempismo (usa mock) o usa librerie come Awaitility for asynchroneous testing.
FuryFart

3
Questa risposta dovrebbe essere rimossa e considerata dannosa. Una risposta molto migliore sotto è stackoverflow.com/a/35163873/1229735
yiati

71

Thread.sleep () potrebbe funzionare nella maggior parte dei casi, ma di solito se stai aspettando, in realtà stai aspettando che si verifichi una particolare condizione o stato. Thread.sleep () non garantisce che tutto ciò che stai aspettando sia effettivamente accaduto.

Ad esempio, se stai aspettando una richiesta di riposo, di solito questa ritorna in 5 secondi, ma se imposti il ​​sonno per 5 secondi il giorno in cui la tua richiesta torna tra 10 secondi, il test fallirà.

Per rimediare a questo JayWay ha una grande utilità chiamata Awatility, perfetta per garantire che si verifichi una condizione specifica prima di andare avanti.

Ha anche una bella api fluente

await().until(() -> 
{
    return yourConditionIsMet();
});  

https://github.com/jayway/awaitility


Non sono riuscito a ottenere l'attesa per la compilazione in Android Studio.
IgorGanapolsky

Hai aggiunto questa riga al tuo file gradle: compile 'org.awaitility: awaitility: 3.0.0'?
Samoht

no. lo aggiungeresti in un caso di test in cui desideri attendere una condizione particolare
Ben Glasser

15

Nel caso in cui il tuo analizzatore di codice statico (come SonarQube) si lamenta, ma non riesci a pensare a un altro modo, piuttosto che dormire, puoi provare con un hack come: Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true); è concettualmente errato, ma è lo stesso di Thread.sleep(1000).

Il modo migliore, ovviamente, è passare un Callable, con la tua condizione appropriata, piuttosto che quello trueche ho.

https://github.com/awaitility/awaitility


@ Jitendra: nota che Awaitility aveva la Durationclasse, ma dalla versione 4 l'hanno rinominata Durations.
Jacob van Lingen,

Un avvertimento, se vuoi che il ritardo del poll sia di 10 secondi o più, assicurati di aumentare il timeout a più di 10 secondi poiché questo è il timeout predefinito e awaitility si lamenterà che il timeout è inferiore al ritardo del poll.
focus

12

È possibile utilizzare la libreria java.util.concurrent.TimeUnit che utilizza internamente Thread.sleep. La sintassi dovrebbe essere simile a questa:

@Test
public void testExipres(){
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExipration("foo", 1000);

    TimeUnit.MINUTES.sleep(2);

    assertNull(sco.getIfNotExipred("foo"));
}

Questa libreria fornisce un'interpretazione più chiara per l'unità di tempo. Puoi utilizzare "HOURS" / "MINUTES" / "SECONDS".


Se avvio un thread di lavoro dall'interno di un test, influirà sleep()sul thread di lavoro?
Anthony Kong,


0

C'è un problema generale: è difficile prendere in giro il tempo. Inoltre, è davvero una cattiva pratica inserire codice a lunga esecuzione / attesa in uno unit test.

Quindi, per rendere testabile un'API di pianificazione, ho utilizzato un'interfaccia con un'implementazione reale e simulata come questa:

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 questo, potresti imitare il tempo nel tuo 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"));
}

Un mocking multi-threading avanzato per Clockè molto più complesso, ovviamente, ma puoi farlo con ThreadLocalriferimenti e una buona strategia di sincronizzazione temporale, ad esempio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.