Como posso fazer um teste JUnit esperar?


93

Eu tenho um teste JUnit que quero esperar por um período de tempo, de forma síncrona. Meu teste JUnit se parece com isto:

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

Eu tentei Thread.currentThread (). Wait (), mas ele lança uma IllegalMonitorStateException (conforme o esperado). Existe algum truque para isso ou eu preciso de um monitor diferente?

Respostas:


117

Que tal Thread.sleep(2000);? :)


14
Se você estiver usando ferramentas de análise de código como o sonarqube, eles reclamarão de Thread.sleepdizer algo como Usar Thread.sleep em um teste geralmente é uma má ideia. Ele cria testes frágeis que podem falhar de forma imprevisível, dependendo do ambiente ("Passa na minha máquina!") Ou da carga. Não confie no tempo (use mocks) ou use bibliotecas como Awaitility para testes assíncronos.
FuryFart

3
Essa resposta deve ser removida e considerada prejudicial. Uma resposta muito melhor abaixo é stackoverflow.com/a/35163873/1229735
yiati

71

Thread.sleep () pode funcionar na maioria dos casos, mas geralmente se você estiver esperando, está na verdade esperando que uma condição ou estado específico ocorra. Thread.sleep () não garante que tudo o que você está esperando realmente aconteceu.

Se você está esperando por uma solicitação de descanso, por exemplo, talvez ele retorne em 5 segundos, mas se você definir seu sono para 5 segundos no dia em que sua solicitação voltar em 10 segundos, seu teste irá falhar.

Para remediar isso, JayWay tem um ótimo utilitário chamado Awatility, que é perfeito para garantir que uma condição específica ocorra antes de você seguir em frente.

Tem uma boa API fluente também

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

https://github.com/jayway/awaitility


Não consegui esperar para compilar no Android Studio.
IgorGanapolsky

Você adicionou esta linha ao arquivo gradle: compile 'org.awaitility: awaitility: 3.0.0'?
Samoht

não. você adicionaria isso em um caso de teste em que deseja esperar por uma condição específica
Ben Glasser

15

No caso do seu analisador de código estático (como SonarQube) reclamar, mas você não conseguir pensar em outra maneira, ao invés de dormir, você pode tentar um hack como: Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true); É conceitualmente incorreto, mas é o mesmo que Thread.sleep(1000).

A melhor maneira, claro, é passar um Callable, com sua condição apropriada, ao invés do trueque eu tenho.

https://github.com/awaitility/awaitility


@Jitendra: Observe que o Awaitility costumava ter a Durationclasse, mas desde a versão 4 eles a renomearam para Durations.
Jacob van Lingen

Uma advertência: se você quiser que o atraso da pesquisa seja de 10 segundos ou mais, certifique-se de aumentar o tempo limite para mais de 10 segundos, pois esse é o tempo limite padrão e a espera reclamará que o tempo limite é menor que o atraso da pesquisa.
foco em

12

Você pode usar a biblioteca java.util.concurrent.TimeUnit que usa Thread.sleep internamente. A sintaxe deve ser semelhante a esta:

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

    TimeUnit.MINUTES.sleep(2);

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

Esta biblioteca fornece uma interpretação mais clara para a unidade de tempo. Você pode usar 'HOURS' / 'MINUTES' / 'SECONDS'.


Se eu iniciar um thread de trabalho de dentro de um teste, isso sleep()afetará o thread de trabalho?
Anthony Kong


0

Há um problema geral: é difícil zombar do tempo. Além disso, é uma prática muito ruim colocar código de longa execução / espera em um teste de unidade.

Portanto, para tornar uma API de agendamento testável, usei uma interface com uma implementação real e uma simulação 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);
    }

}

Com isso, você pode imitar o tempo em seu teste:

@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"));
}

Um mock avançado multi-threading para Clocké muito mais complexo, é claro, mas você pode fazer isso com ThreadLocalreferências e uma boa estratégia de sincronização de tempo, por exemplo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.