非同步程式設計測試Awaitlity簡介| Baeldung
非同步系統的一個常見問題是,很難為那些專注於業務邏輯並且不會受到同步,超時和併發控制汙染的可編寫測試。
在本文中,我們將介紹Awaitility - 一個為非同步系統測試提供簡單的特定於域的語言(DSL)的庫。
透過Awaitility,我們可以透過易於閱讀的DSL表達我們對系統的期望。
我們需要將Awaitility依賴項新增到我們的pom.xml中。
該awaitility庫將足以滿足大多數使用情況。如果我們想要使用基於代理的條件,我們還需要提供awaitility-proxy庫:
<dependency> <groupId>org.awaitility</groupId> <artifactId>awaitility</artifactId> <version>3.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.awaitility</groupId> <artifactId>awaitility-proxy</artifactId> <version>3.0.0</version> <scope>test</scope> </dependency> |
您可以在Maven Central上找到最新版本的awaitility和awaitility-proxy庫。
建立非同步服務
讓我們編寫一個簡單的非同步服務並測試它:
public class AsyncService { private final int DELAY = 1000; private final int INIT_DELAY = 2000; private AtomicLong value = new AtomicLong(0); private Executor executor = Executors.newFixedThreadPool(4); private volatile boolean initialized = false; void initialize() { executor.execute(() -> { sleep(INIT_DELAY); initialized = true; }); } boolean isInitialized() { return initialized; } void addValue(long val) { throwIfNotInitialized(); executor.execute(() -> { sleep(DELAY); value.addAndGet(val); }); } public long getValue() { throwIfNotInitialized(); return value.longValue(); } private void sleep(int delay) { try { Thread.sleep(delay); } catch (InterruptedException e) { } } private void throwIfNotInitialized() { if (!initialized) { throw new IllegalStateException("Service is not initialized"); } } } |
現在,讓我們建立測試類:
public class AsyncServiceTest { private AsyncService asyncService; @Before public void setUp() { asyncService = new AsyncService(); } //... } |
我們的測試在呼叫initialize方法後檢查我們的服務初始化是否在指定的超時時間(預設為10秒)內發生。
此測試用例僅等待服務初始化狀態更改或在未發生狀態更改時丟擲ConditionTimeoutException。
狀態由Callable獲得,Callable在指定的初始延遲(預設為100毫秒)後以定義的時間間隔(預設為100毫秒)輪詢我們的服務。這裡我們使用超時,間隔和延遲的預設設定:
asyncService.initialize(); await() .until(asyncService::isInitialized); |
在這裡,我們使用await - Awaitility類的靜態方法之一。它返回ConditionFactory類的例項。我們也可以用其他的方法,如給予增加可讀性的原因。
可以使用Awaitility類中的靜態方法更改預設計時引數:
Awaitility.setDefaultPollInterval(10, TimeUnit.MILLISECONDS); Awaitility.setDefaultPollDelay(Duration.ZERO); Awaitility.setDefaultTimeout(Duration.ONE_MINUTE); |
在這裡我們可以看到Duration類的使用,它為最常用的時間段提供了有用的常量。
我們還可以為每個等待呼叫提供自定義時間值。在這裡,我們期望初始化將在五秒後發生,並且至少在100ms之後,輪詢間隔為100ms:
asyncService.initialize(); await() .atLeast(Duration.ONE_HUNDRED_MILLISECONDS) .atMost(Duration.FIVE_SECONDS) .with() .pollInterval(Duration.ONE_HUNDRED_MILLISECONDS) .until(asyncService::isInitialized); |
值得一提的是,ConditionFactory包含像其他方法with, then, and, given。這些方法沒有做任何事情,只是返回它,但它們可能有助於增強測試條件的可讀性。
使用匹配器:
Awaitility還允許使用hamcrest匹配器來檢查表示式的結果。例如,我們可以在呼叫addValue方法後檢查我們的long值是否按預期更改:
asyncService.initialize(); await() .until(asyncService::isInitialized); long value = 5; asyncService.addValue(value); await() .until(asyncService::getValue, equalTo(value)); |
請注意,在此示例中,我們使用第一個await呼叫等待服務初始化。否則,getValue方法將丟擲IllegalStateException。
忽略例外:
有時,我們遇到一種情況,即方法在非同步作業完成之前丟擲異常。在我們的服務中,它可以是在初始化服務之前呼叫getValue方法。
Awaitility提供了在不失敗測試的情況下忽略此異常的可能性。
例如,讓我們在初始化之後檢查getValue結果是否等於零,忽略IllegalStateException:
asyncService.initialize(); given().ignoreException(IllegalStateException.class) .await().atMost(Duration.FIVE_SECONDS) .atLeast(Duration.FIVE_HUNDRED_MILLISECONDS) .until(asyncService::getValue, equalTo(0L)); |
使用代理:
我們使用awaitility-proxy以實現基於代理的條件判斷。代理的想法是為條件提供真正的方法呼叫,而不實現Callable或lambda表示式。
讓我們使用AwaitilityClassProxy.to靜態方法來檢查AsyncService是否已初始化:
asyncService.initialize(); await() .untilCall(to(asyncService).isInitialized(), equalTo(true)); |
訪問欄位:
Awaitility甚至可以訪問私有欄位來對它們執行斷言。在以下示例中,我們可以看到另一種獲取服務初始化狀態的方法:
asyncService.initialize(); await() .until(fieldIn(asyncService) .ofType(boolean.class) .andWithName("initialized"), equalTo(true)); |
如何測試@Scheduled
Spring Framework中的一個可用註釋是@Scheduled。我們可以使用此批註以預定的方式執行任務。
讓我們開始建立一個簡單的Counter類:
@Component public class Counter { private AtomicInteger count = new AtomicInteger(0); @Scheduled(fixedDelay = 5) public void scheduled() { this.count.incrementAndGet(); } public int getInvocationCount() { return this.count.get(); } } |
我們將使用預定的方法來增加計數。請注意,我們還新增了@Scheduled註釋,以在5毫秒的固定時間內執行它。
另外,讓我們建立一個ScheduledConfig類,使用@EnableScheduling註釋啟用計劃任務:
@Configuration @EnableScheduling @ComponentScan("com.baeldung.scheduled") public class ScheduledConfig { } |
我們可以使用Awaitility DSL使我們的測試更具說明性:
@SpringJUnitConfig(ScheduledConfig.class) public class ScheduledAwaitilityIntegrationTest { @SpyBean private Counter counter; @Test public void whenWaitOneSecond_thenScheduledIsCalledAtLeastTenTimes() { await() .atMost(Duration.ONE_SECOND) .untilAsserted(() -> verify(counter, atLeast(10)).scheduled()); } } |
在這種情況下,我們使用@SpyBean註釋注入bean,以檢查在 一秒的時間內呼叫排程方法的次數。
完整原始碼GitHub上獲得
相關文章
- javascript非同步程式設計幾種方法簡介JavaScript非同步程式設計
- Rust語言非同步程式設計簡介 - ShakaibRust非同步程式設計AI
- Netflix Mantis簡介 - Baeldung
- Evrete 規則引擎簡介 | baeldungVR
- python 網路程式設計----非阻塞或非同步程式設計Python程式設計非同步
- [譯] 非同步程式設計:阻塞與非阻塞非同步程式設計
- windows程式設計簡介Windows程式設計
- shell程式設計簡介程式設計
- Socket程式設計中的同步、非同步、阻塞和非阻塞(轉)程式設計非同步
- IO程式設計和NIO程式設計簡介程式設計
- 002 Rust 非同步程式設計,async await 簡單介紹Rust非同步程式設計AI
- 測試CMS同步測試CMS同步測試CMS同步
- shell程式設計—簡介(一)程式設計
- 結對程式設計簡介程式設計
- 簡單理解非同步程式設計(python)和非同步程式設計(nodejs)非同步程式設計PythonNodeJS
- 測試管理流程簡介
- 阿里巴巴哨兵Sentinel簡介 | Baeldung阿里
- Apache Kafka資料模型概念簡介 - BaeldungApacheKafka模型
- 005 Rust 非同步程式設計,Pin 介紹Rust非同步程式設計
- Linux Shell程式設計(1)——shell程式設計簡介Linux程式設計
- 阻塞/非阻塞讀寫總結、tcp網路程式設計的本質、muduo::Buffer設計簡介TCP程式設計
- Scala 簡介 [摘自 Scala程式設計 ]程式設計
- Linux Socket 程式設計簡介Linux程式設計
- WebGL程式設計指南(1)簡介Web程式設計
- BASH SHELL 程式設計簡介(轉)程式設計
- 合同測試簡介 - Elizabeth Fiennes
- [雪峰磁針石部落格]滲透測試簡介1滲透測試簡介
- 006 Rust 非同步程式設計,Stream 介紹Rust非同步程式設計
- CCUT程式設計能力測試---前言程式設計
- 函數語言程式設計簡介函數程式設計
- .NET泛型程式設計簡介 (轉)泛型程式設計
- 【進階之路】併發程式設計(三)-非阻塞同步機制程式設計
- Windows 程式設計簡介從C/C++到Windows程式設計Windows程式設計C++
- app 裡的 A/B 測試簡介APP
- 精準化測試原理簡介
- Dart 非同步程式設計相關概念簡述Dart非同步程式設計
- Node.js 非同步程式設計之 Callback介紹Node.js非同步程式設計
- 一文徹底搞定(阻塞/非阻塞/同步/非同步)網路IO、併發程式設計模型、非同步程式設計模型的愛恨情仇非同步程式設計模型