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 ThreadLocal
referências e uma boa estratégia de sincronização de tempo, por exemplo.
Thread.sleep
dizer 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.