Как я могу заставить тест JUnit ждать?


95

У меня есть тест 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 (как и ожидалось).

Есть какой-то трюк или мне нужен другой монитор?

Ответы:


119

Как насчет Thread.sleep(2000);? :)


15
Если вы используете инструменты анализа кода, такие как sonarqube, они будут жаловаться, что Thread.sleepговорят что-то вроде использования Thread.sleep в тесте, как правило, плохая идея. Он создает нестабильные тесты, которые могут непредсказуемо завершиться ошибкой в ​​зависимости от среды («Проходит на моей машине!») Или нагрузки. Не полагайтесь на время (используйте mocks) или используйте библиотеки, такие как Awaitility, для асинхронного тестирования.
FuryFart

4
Этот ответ следует удалить и считать вредным. Гораздо лучший ответ ниже - stackoverflow.com/a/35163873/1229735
yiati

73

Thread.sleep () может работать в большинстве случаев, но обычно, если вы ждете, вы фактически ждете наступления определенного условия или состояния. Thread.sleep () не гарантирует, что все, чего вы ждете, действительно произошло.

Например, если вы ожидаете запроса на отдых, возможно, он обычно возвращается через 5 секунд, но если вы установите режим сна на 5 секунд в день, когда ваш запрос вернется через 10 секунд, ваш тест завершится неудачей.

Чтобы исправить это, у JayWay есть отличная утилита под названием Awatility, которая идеально подходит для обеспечения того, чтобы определенное состояние возникло, прежде чем вы двинетесь дальше.

У него также хороший свободный API

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

https://github.com/jayway/awaitility


Я не мог получить возможность компиляции в Android Studio.
Игорь Ганапольский

Вы добавили эту строку в свой файл gradle: compile 'org.awaitility: awaitility: 3.0.0'?
Samoht

нет. вы бы добавили это в тестовом случае, когда вы хотите дождаться определенного условия
Бен Глассер

16

В случае, если ваш статический анализатор кода (например, SonarQube) жалуется, но вы не можете придумать другой способ, кроме сна, вы можете попробовать взломать, например: Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true); Это концептуально неверно, но это то же самое, что и Thread.sleep(1000).

Лучший способ, конечно, - передать Callable с вашим соответствующим условием, а не тем true, которое есть у меня.

https://github.com/awaitility/awaitility


@Jitendra: обратите внимание, что раньше у Awaitility был Durationкласс, но с версии 4 они переименовали его в Durations.
Якоб ван Линген

Одно предостережение: если вы хотите, чтобы задержка опроса составляла 10 секунд или более, обязательно увеличьте время ожидания до более 10 секунд, поскольку это время ожидания по умолчанию, и awaitility будет жаловаться, что время ожидания меньше задержки опроса.
focus

15

Вы можете использовать библиотеку 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()на рабочий поток?
Энтони Конг


0

Есть общая проблема: издеваться над временем сложно. Кроме того, очень плохая практика помещать в модульный тест длинный исполняющийся / ожидающий код.

Итак, чтобы сделать 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ссылок и хорошей стратегии синхронизации времени.


0

Если это абсолютно необходимо, чтобы вызвать задержку в тесте CountDownLatch, это простое решение. В своем тестовом классе объявите:

private final CountDownLatch waiter = new CountDownLatch(1);

и в тесте, где необходимо:

waiter.await(1000 * 1000, TimeUnit.NANOSECONDS); // 1ms

Может быть, излишне говорить, но помните, что вам следует сократить время ожидания и не накапливать время ожидания во многих местах.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.