Як я можу змусити зачекати тест JUnit?


94

У мене є тест 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 (як очікувалося).

У цьому є якась хитрість чи мені потрібен інший монітор?

Відповіді:


118

Як щодо Thread.sleep(2000);? :)


15
Якщо ви використовуєте інструменти для аналізу коду, такі як sonarqube, вони будуть скаржитися на Thread.sleepте, що кажуть щось на зразок Використання Thread.sleep в тесті - це взагалі погана ідея. Це створює крихкі тести, які можуть непередбачувано провалитися залежно від середовища ("Проходить на моїй машині!") Або навантаження. Не покладайтесь на хронометраж (використовуйте макети) або використовуйте бібліотеки, такі як 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'?
Самохт

немає. ви б додали це у тестовому випадку, коли ви хочете дочекатися певної умови
Бен Глассер

16

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

Звичайно, найкращий спосіб - це пройти Виклику з вашим відповідним станом, а не таким true, який я маю.

https://github.com/awaitility/awaitility


@Jitendra: Зверніть увагу, що раніше Durationклас мав Awaitility , але з версії 4 вони перейменували його на Durations.
Якоб ван Лінген,

Одним застереженням, якщо ви хочете, щоб затримка опитування становила 10 секунд або більше, не забудьте збільшити час очікування до більш ніж 10 секунд, оскільки це тайм-аут за замовчуванням, і очікуваність скаржиться, що час очікування менше, ніж затримка опитування.
фокус

14

Ви можете використовувати бібліотеку 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.