Есть общая проблема: издеваться над временем сложно. Кроме того, очень плохая практика помещать в модульный тест длинный исполняющийся / ожидающий код.
Итак, чтобы сделать API планирования тестируемым, я использовал интерфейс с реальной и фиктивной реализацией, например:
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);
}
}
С его помощью вы можете имитировать время в своем тесте:
@Test
public void testExipres() {
MockClock clock = new MockClock();
SomeCacheObject sco = new SomeCacheObject();
sco.putWithExipration("foo", 1000);
clock.sleep(2000)
assertNull(sco.getIfNotExpired("foo"));
}
Расширенный многопоточный макет Clock
, конечно, намного сложнее, но вы можете сделать его, например, с помощью ThreadLocal
ссылок и хорошей стратегии синхронизации времени.
Thread.sleep
говорят что-то вроде использования Thread.sleep в тесте, как правило, плохая идея. Он создает нестабильные тесты, которые могут непредсказуемо завершиться ошибкой в зависимости от среды («Проходит на моей машине!») Или нагрузки. Не полагайтесь на время (используйте mocks) или используйте библиотеки, такие как Awaitility, для асинхронного тестирования.