У меня есть тест JUnit, который я хочу подождать некоторое время синхронно. Мой тест JUnit выглядит так:
@Test
public void testExipres(){
SomeCacheObject sco = new SomeCacheObject();
sco.putWithExipration("foo", 1000);
// WAIT FOR 2 SECONDS
assertNull(sco.getIfNotExipred("foo"));
}
Я пробовал Thread.currentThread().wait()
, но возникает исключение IllegalMonitorStateException (как и ожидалось).
Есть какой-то трюк или мне нужен другой монитор?
java
junit
thread-safety
Кайлар
источник
источник
Thread.sleep
говорят что-то вроде использования Thread.sleep в тесте, как правило, плохая идея. Он создает нестабильные тесты, которые могут непредсказуемо завершиться ошибкой в зависимости от среды («Проходит на моей машине!») Или нагрузки. Не полагайтесь на время (используйте mocks) или используйте библиотеки, такие как Awaitility, для асинхронного тестирования.Thread.sleep () может работать в большинстве случаев, но обычно, если вы ждете, вы фактически ждете наступления определенного условия или состояния. Thread.sleep () не гарантирует, что все, чего вы ждете, действительно произошло.
Например, если вы ожидаете запроса на отдых, возможно, он обычно возвращается через 5 секунд, но если вы установите режим сна на 5 секунд в день, когда ваш запрос вернется через 10 секунд, ваш тест завершится неудачей.
Чтобы исправить это, у JayWay есть отличная утилита под названием Awatility, которая идеально подходит для обеспечения того, чтобы определенное состояние возникло, прежде чем вы двинетесь дальше.
У него также хороший свободный API
await().until(() -> { return yourConditionIsMet(); });
https://github.com/jayway/awaitility
источник
В случае, если ваш статический анализатор кода (например, SonarQube) жалуется, но вы не можете придумать другой способ, кроме сна, вы можете попробовать взломать, например:
Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true);
Это концептуально неверно, но это то же самое, что иThread.sleep(1000)
.Лучший способ, конечно, - передать Callable с вашим соответствующим условием, а не тем
true
, которое есть у меня.https://github.com/awaitility/awaitility
источник
Duration
класс, но с версии 4 они переименовали его вDurations
.Вы можете использовать библиотеку java.util.concurrent.TimeUnit, которая внутренне использует Thread.sleep. Синтаксис должен выглядеть так:
@Test public void testExipres(){ SomeCacheObject sco = new SomeCacheObject(); sco.putWithExipration("foo", 1000); TimeUnit.MINUTES.sleep(2); assertNull(sco.getIfNotExipred("foo")); }
Эта библиотека обеспечивает более четкую интерпретацию единицы времени. Вы можете использовать «ЧАСЫ» / «МИНУТЫ» / «СЕКУНДЫ».
источник
sleep()
на рабочий поток?Вы также можете использовать
CountDownLatch
объект, как описано здесь .источник
Есть общая проблема: издеваться над временем сложно. Кроме того, очень плохая практика помещать в модульный тест длинный исполняющийся / ожидающий код.
Итак, чтобы сделать 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) // WAIT FOR 2 SECONDS assertNull(sco.getIfNotExpired("foo")); }
Расширенный многопоточный макет
Clock
, конечно, намного сложнее, но вы можете сделать его, например, с помощьюThreadLocal
ссылок и хорошей стратегии синхронизации времени.источник
Если это абсолютно необходимо, чтобы вызвать задержку в тесте
CountDownLatch
, это простое решение. В своем тестовом классе объявите:private final CountDownLatch waiter = new CountDownLatch(1);
и в тесте, где необходимо:
waiter.await(1000 * 1000, TimeUnit.NANOSECONDS); // 1ms
Может быть, излишне говорить, но помните, что вам следует сократить время ожидания и не накапливать время ожидания во многих местах.
источник