如何进行JUnit测试等待?

时间:2013-04-10 23:53:30

标签: java junit thread-safety

我有一个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(如预期的那样)。是否有一些技巧,或者我需要一个不同的显示器?

7 个答案:

答案 0 :(得分:107)

Thread.sleep(2000);怎么样? :)

答案 1 :(得分:49)

Thread.sleep()在大多数情况下都可以工作,但通常如果您正在等待,您实际上正在等待某个特定条件或状态发生。 Thread.sleep()并不能保证您等待的任何事情确实发生了。

如果您正在等待休息请求,例如它通常会在5秒内返回,但如果您在10秒内将请求恢复为5秒钟,那么您的测试将会失败。

为了解决这个问题,JayWay有一个很棒的实用工具Awatility,它非常适合在你继续前进之前确保特定情况发生。

它还有一个很好的流畅api

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

https://github.com/jayway/awaitility

答案 2 :(得分:4)

您也可以像解释here一样使用CountDownLatch对象。

答案 3 :(得分:2)

如果您的静态代码分析器(如SonarQube)投诉,但您无法想到另一种方法,除了睡觉,您可以尝试以下黑客: Awaitility.await().pollDelay(Duration.ONE_SECOND).until(() -> true); 从概念上讲这是不正确的,但与Thread.sleep(1000)相同。

当然,最好的方法是传递具有适当条件的Callable而不是我拥有的true

https://github.com/awaitility/awaitility

答案 4 :(得分:2)

您可以使用java.util.concurrent.TimeUnit库,该库在内部使用Thread.sleep。语法应如下所示:

@click.native.prevent

该库为时间单位提供了更清晰的解释。您可以使用“小时” /“分钟” /“秒”。

答案 5 :(得分:2)

如果绝对必须在测试CountDownLatch中产生延迟,则这是一个简单的解决方案。在测试类中声明:

private final CountDownLatch waiter = new CountDownLatch(1);

,并在需要的地方进行测试:

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

也许不必多说,但要记住,应将等待时间保持在很小的范围内,而不要将等待时间累积到太多地方。

答案 6 :(得分: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引用和良好的时间同步策略来实现它。