如何使JUnit测试等待?

b91juud3  于 2022-11-11  发布在  其他
关注(0)|答案(9)|浏览(209)

我有一个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(),但它抛出了非法监视器状态异常(如预期)。
有什么窍门吗?还是我需要一个不同的显示器?

j7dteeu8

j7dteeu81#

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

mftmpeh8

mftmpeh82#

Thread.sleep()可以在大多数情况下工作,但通常如果你在等待,你实际上是在等待一个特定的条件或状态发生。Thread.sleep()不能保证你所等待的任何事情实际上已经发生了。
例如,如果您正在等待一个休息请求,它通常会在5秒内返回,但如果您将睡眠时间设置为5秒,而当天您的请求会在10秒内返回,则测试将失败。
为了解决这个问题,JayWay有一个很棒的实用程序,叫做Awatility,它非常适合确保在你继续前进之前出现特定的情况。
它有一个很好的流畅的api以及

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

https://github.com/jayway/awaitility

q7solyqu

q7solyqu3#

如果你的静态代码分析器(如SonarQube)投诉,但你不能想到另一种方式,而不是睡觉,你可以尝试与黑客一样:Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true);这在概念上是不正确的,但它与Thread.sleep(1000)相同。
当然,最好的方法是用适当的条件传递Callable,而不是我所拥有的true
https://github.com/awaitility/awaitility

cu6pst1q

cu6pst1q4#

您可以使用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"));
}

此库为时间单位提供了更清晰解释您可以使用'HOURS'/'MINUTES'/'SECONDS'

h9a6wy2h

h9a6wy2h5#

如果在一个测试中绝对必须生成延迟CountDownLatch是一个简单的解决方案.在你的测试类中声明:

private final CountDownLatch waiter = new CountDownLatch(1);

在需要的测试中:

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

也许没有必要说,但请记住,你应该保持等待时间小,而不是累积等待太多的地方。

jdgnovmf

jdgnovmf6#

您也可以使用CountDownLatch对象,如here所述。

kuuvgm7e

kuuvgm7e7#

Mockito(已经通过Sping Boot 项目的可传递依赖项提供)有几种方法来等待异步事件,分别是发生的条件。
一个目前非常适合我们的简单模式是:

// ARRANGE – instantiate Mocks, setup test conditions

// ACT – the action to test, followed by:
Mockito.verify(myMockOrSpy, timeout(5000).atLeastOnce()).delayedStuff();
// further execution paused until `delayedStuff()` is called – or fails after timeout

// ASSERT – assertThat(...)

this article(作者:@ fernando-cejas)中描述了两个稍微复杂但更为复杂的
我的紧急建议,关于目前的热门答案给在这里:您希望您的测试
1.尽快完成
1.具有一致的结果,与测试环境无关(非“片状”)
...所以不要在测试代码中使用Thread.sleep()
相反,让您的产品代码使用依赖注入(或者,稍微“脏一点”,公开一些可模仿/可监视的方法),然后使用Mockito、Awaitly、ConcurrentUnit或其他方法来确保在Assert发生之前满足异步前提条件。

z2acfund

z2acfund8#

还有一个普遍的问题:模拟时间是很困难的,而且,在单元测试中放置长时间运行/等待的代码也是很糟糕的做法。
因此,为了使调度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 testExpiration() {
    MockClock clock = new MockClock();
    SomeCacheObject sco = new SomeCacheObject();
    sco.putWithExpiration("foo", 1000);
    clock.sleep(2000) // wait for 2 seconds
    assertNull(sco.getIfNotExpired("foo"));
}

当然,针对Clock的高级多线程模拟要复杂得多,但是您可以使用ThreadLocal引用和良好的时间同步策略来实现它。

pcrecxhr

pcrecxhr9#

在测试中使用Thread.sleep并不是一个好的做法。它会创建脆弱的测试,根据环境(“Passes on my machine!”)或负载的不同,这些测试可能会不可预测地失败。不要依赖于计时(使用mock)或使用Awaitility之类的库进行异步测试。
Dependency : testImplementation 'org.awaitility:awaitility:3.0.0'

await().pollInterval(Duration.FIVE_SECONDS).atLeast(Duration.FIVE_SECONDS).atMost(Duration.FIVE_SECONDS).untilAsserted(() -> {
      // your assertion
    });

相关问题