本系列文章索引《響應式Spring的道法術器》
前情提要:Reactor 3快速上手 | 響應式流規範
本文測試源碼java
在很是重視DevOps的今天,以及一些奉行TDD的團隊中,自動化測試是保證代碼質量的重要手段。要進行Reactor的測試,首先要確保添加reactor-test
依賴。react
reactor-test 用 Maven 配置 <dependencies>git
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.1.4.RELEASE</version>
<scope>test</scope>
</dependency>githubreactor-test 用 Gradle 配置app
dependencies {
testCompile 'io.projectreactor:reactor-test:3.1.4.RELEASE'
}ide
1.3.2.3節初步介紹了關於StepVerifier
的用法。舉個例子回憶一下:測試
@Test public void testAppendBoomError() { Flux<String> source = Flux.just("foo", "bar"); StepVerifier.create( appendBoomError(source)) .expectNext("foo") .expectNext("bar") .expectErrorMessage("boom") .verify(); }
咱們一般使用create
方法建立基於Flux或Mono的StepVerifier
,而後就能夠進行如下測試:線程
expectNext(T...)
或expectNextCount(long)
。`consumeNextWith(Consumer<T>)
。thenAwait(Duration)
和then(Runnable)
。對於終止事件,相應的指望方法(如expectComplete()
、expectError()
,及其全部的變體方法) 使用以後就不能再繼續增長別的指望方法了。最後你只能對 StepVerifier 進行一些額外的配置並 觸發校驗(一般調用verify()
及其變體方法)。code
從StepVerifier
內部實現來看,它訂閱了待測試的 Flux 或 Mono,而後將序列中的每一個信號與測試 場景的指望進行比對。若是匹配的話,測試成功。若是有不匹配的狀況,則拋出AssertionError
異常。blog
響應式流是一種基於時間的數據流。許多時候,待測試的數據流存在延遲,從而持續一段時間。若是這種場景比較多的話,那麼會致使自動化測試運行時間較長。所以StepVerifier
提供了能夠操做「虛擬時間」的測試方式,這時候須要使用StepVerifier.withVirtualTime
來構造。
爲了提升 StepVerifier 正常起做用的機率,它通常不接收一個簡單的 Flux 做爲輸入,而是接收 一個Supplier
,從而能夠在配置好訂閱者以後 「懶建立」待測試的 flux,如:
StepVerifier.withVirtualTime(() -> Mono.delay(Duration.ofDays(1))) //... 繼續追加指望方法
有兩種處理時間的指望方法,不管是否配置虛擬時間都是可用的:
thenAwait(Duration)
會暫停校驗步驟(容許信號延遲發出)。expectNoEvent(Duration)
一樣讓序列持續必定的時間,期間若是有任何信號發出則測試失敗。在普通的測試中,兩個方法都會基於給定的持續時間暫停線程的執行。而若是是在虛擬時間模式下就相應地使用虛擬時間。
StepVerifier.withVirtualTime(() -> Mono.delay(Duration.ofDays(1))) .expectSubscription() // 1 .expectNoEvent(Duration.ofDays(1)) // 2 .expectNext(0L) .verifyComplete(); // 3
expectSubscription().expectNoEvent(duration)
來代替;verify
或變體方法最終會返回一個Duration
,這是實際的測試時長。可見,withVirtualTime使咱們不用實際等1天來完成測試了。
虛擬時間的功能是如何實現的呢?StepVerifier.withVirtualTime
會在Reactor的調度器工廠方法中插入一個自定義的調度器VirtualTimeScheduler
來代替默認調度器(那些基於時間的操做符一般默認使用Schedulers.parallel()
調度器)。
一般狀況下,使用StepVerifier
的expect*
就能夠搞定多數的測試場景了。可是,它也有機關用盡的時候,好比下邊這個特殊的例子:
private Mono<String> executeCommand(String command) { // 基於command執行一些操做,執行完成後返回Mono<String> } public Mono<Void> processOrFallback(Mono<String> commandSource, Mono<Void> doWhenEmpty) { return commandSource .flatMap(command -> executeCommand(command).then()) // 1 .switchIfEmpty(doWhenEmpty); // 2 }
then()
會忽略全部的元素,只保留完成信號,因此返回值爲Mono<Void>;1和2都是Mono<Void>
,這時候就比較難判斷processOfFallback
中具體執行了哪條路徑。這時候能夠用log()
或doOn*()
等方法來觀察,但這「在非綠即紅」的單測中不起做用。或者在某個路徑加入標識狀態的值,並經過判斷狀態值是否正確來肯定,但這就須要修改被測試的processOfFallback
的代碼了。
Reactor版本 3.1.0 以後咱們可使用PublisherProbe
來作相似場景的驗證。以下:
@Test public void testWithPublisherProbe() { PublisherProbe<Void> probe = PublisherProbe.empty(); // 1 StepVerifier.create(processOrFallback(Mono.empty(), probe.mono())) // 2 .verifyComplete(); probe.assertWasSubscribed(); // 3 probe.assertWasRequested(); // 4 probe.assertWasNotCancelled(); // 5 }
TestPublisher
本質上是一個Publisher
,不過使用它能更加「自由奔放」地發出各類元素,以便進行各類場景的測試。
1)「自由」地發出元素
咱們能夠用它提供的方法發出各類信號:
next(T)
以及 next(T, T...)
發出 1-n 個 onNext 信號。emit(T...)
起一樣做用,而且會執行 complete()。complete()
會發出終止信號 onComplete。error(Throwable)
會發出終止信號 onError。好比:
@Test public void testWithTestPublisher() { TestPublisher<Integer> testPublisher = TestPublisher.<Integer>create().emit(1, 2, 3); StepVerifier.create(testPublisher.flux().map(i -> i * i)) .expectNext(1, 4, 9) .expectComplete(); }
2)「奔放」地發出元素
使用create
工廠方法就能夠獲得一個正常的TestPublisher
。而使用createNonCompliant
工廠方法能夠建立一個「不正常」的TestPublisher
。後者須要傳入由TestPublisher.Violation
枚舉指定的一組選項,這些選項可用於告訴 publisher 忽略哪些問題。枚舉值有:
REQUEST_OVERFLOW
: 容許 next 在請求不足的時候也能夠調用,而不會觸發 IllegalStateException。ALLOW_NULL
: 容許 next 可以發出一個 null 值而不會觸發 NullPointerException。CLEANUP_ON_TERMINATE
: 能夠重複屢次發出終止信號,包括 complete()、error() 和 emit()。不過這個功能可能更多地是給Reactor項目開發者自己使用的,好比當他們開發了一個新的操做符,能夠用這種方式來測試這個操做符是否知足響應式流的規範。
3)TestPublisher也是個PublisherProbe
更讚的是,TestPublisher
實現了PublisherProbe
接口,意味着咱們還可使用它提供的assert*
方法來跟蹤其內部的訂閱和執行狀態。