我正在研究一个使用Spring WebFlux堆栈的项目。我们有一个Controller,您可以在其中订阅特定对象的更新。该控制器返回一个EmitterProcessor
客户端可以在其中订阅。在EmitterProcessor
上发布内容时,订阅的客户端会收到通知。
这在实践中效果很好,但是我的单元测试失败了。单元测试使用WebTestClient,它在exchange()
操作返回EmitterProcessor
时阻塞(也尝试使用其他FluxProcessor
实现,例如UnicastProcessor
)。我得到的错误如下:
java.lang.IllegalStateException:阻止读取5000 MILLISECONDS超时
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:117)
at reactor.core.publisher.Mono.block(Mono.java:1524)
at org.springframework.test.web.reactive.server.DefaultWebTestClient$DefaultRequestBodyUriSpec.exchange(DefaultWebTestClient.java:283)
我发现了这个thread,它也报告了exchange()
块上的WebTestClient
方法,但是正如那里所解释的,它仅用于检索状态和标头,因此不应是问题。另外,在返回Flux
的情况下,这可以正常工作,如引用的testcase所示。
我创建了一个从引用的测试用例派生的简单测试用例,并将其修改为在一种情况下返回Flux
,在另一种情况下返回EmitterProcessor
(失败)。您可能会注意到,EmitterProcessor
的断言应该使它失败,但是由于阻塞了exchange()
调用,所以它永远不会到达那里。
还要注意,当我取消注释行processor.onNext("hello");
时,将返回此内容,并且测试用例成功。
package test;
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM;
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;
import org.junit.Test;
import org.springframework.test.web.reactive.server.FluxExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.EmitterProcessor;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
public class ProcessorTest {
private final WebTestClient client = WebTestClient.bindToController(new StringController()).configureClient().build();
@Test
public void entityStream() {
FluxExchangeResult<String> result = client //
.get().uri("/flux") //
.accept(TEXT_EVENT_STREAM) //
.exchange() //
.expectStatus().isOk() //
.expectHeader().contentTypeCompatibleWith(TEXT_EVENT_STREAM) //
.returnResult(String.class);
StepVerifier.create(result.getResponseBody()) //
.expectNext("hello") //
.thenCancel() //
.verify();
}
@Test
public void entityStreamProcessor() {
FluxExchangeResult<String> result = client //
.get().uri("/processor") //
.accept(TEXT_EVENT_STREAM) //
.exchange() //
.expectStatus().isOk() //
.expectHeader().contentTypeCompatibleWith(TEXT_EVENT_STREAM) //
.returnResult(String.class);
StepVerifier.create(result.getResponseBody()) //
.expectNext("hello") //
.thenCancel() //
.verify();
}
@RestController
static class StringController {
@GetMapping(value = "/flux", produces = TEXT_EVENT_STREAM_VALUE)
Flux<String> getFlux() {
return Flux.just("hello");
}
@GetMapping(value = "/processor", produces = TEXT_EVENT_STREAM_VALUE)
Flux<String> getProcessor() {
EmitterProcessor<String> processor = EmitterProcessor.create();
// processor.onNext("hello");
return processor;
}
}
}