[英]How can I make a JUnit test wait?
我有一個 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 (如預期的那樣)。
有什么技巧還是我需要不同的顯示器?
Thread.sleep(2000);
怎么樣? ? :)
Thread.sleep() 在大多數情況下都可以工作,但通常如果您在等待,您實際上是在等待特定條件或狀態發生。 Thread.sleep() 不能保證您等待的任何事情都已實際發生。
例如,如果您正在等待休息請求,它通常會在 5 秒內返回,但如果您將睡眠時間設置為 5 秒,那么您的請求會在 10 秒后返回,您的測試將失敗。
為了解決這個問題,JayWay 有一個名為Awatility的強大實用程序,它非常適合確保在您繼續前進之前發生特定情況。
它也有一個很好的流利的 api
await().until(() ->
{
return yourConditionIsMet();
});
如果您的靜態代碼分析器(如 SonarQube)抱怨,但您想不出其他方法,而不是睡眠,您可以嘗試使用類似的 hack: Awaitility.await().pollDelay(Durations.ONE_SECOND).until(() -> true);
它在概念上是不正確的,但它與Thread.sleep(1000)
相同。
當然,最好的方法是使用適當的條件傳遞一個 Callable,而不是我擁有的true
。
您可以使用內部使用 Thread.sleep 的 java.util.concurrent.TimeUnit 庫。 語法應如下所示:
@Test
public void testExipres(){
SomeCacheObject sco = new SomeCacheObject();
sco.putWithExipration("foo", 1000);
TimeUnit.MINUTES.sleep(2);
assertNull(sco.getIfNotExipred("foo"));
}
這個庫為時間單位提供了更清晰的解釋。 您可以使用“小時”/“分鍾”/“秒”。
如果絕對必須在測試中產生延遲, CountDownLatch
是一個簡單的解決方案。 在您的測試類中聲明:
private final CountDownLatch waiter = new CountDownLatch(1);
並在需要的測試中:
waiter.await(1000 * 1000, TimeUnit.NANOSECONDS); // 1ms
也許沒必要說,但請記住,您應該保持較小的等待時間,而不是累積等待太多地方。
您還可以使用此處解釋的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 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
引用和良好的時間同步策略來實現它。
Mockito (已經通過 Spring 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(...)
@fernando-cejas在這篇文章中描述了兩個稍微復雜但更復雜的
我對此處給出的當前最佳答案的緊急建議:您希望您的測試
...所以不要在測試代碼中使用Thread.sleep()
來犯傻。
相反,讓您的生產代碼使用依賴注入(或者,有點“骯臟”,公開一些可模擬/可監視的方法),然后使用 Mockito、 Awaitly 、 ConcurrentUnit或其他方法來確保在斷言發生之前滿足異步先決條件。
在測試中使用 Thread.sleep 不是一個好習慣。 它會創建脆弱的測試,這些測試可能會因環境(“通過我的機器!”)或負載而無法預測地失敗。 不要依賴時間(使用模擬)或使用諸如 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
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.