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.