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 ThreadLocal
riferimenti e una buona strategia di sincronizzazione temporale, ad esempio.
Thread.sleep
dire 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.