当控制器返回EmitterProcessor而不是Flux时,WebTestClient在单元测试中阻塞

时间:2019-01-30 13:19:54

标签: spring unit-testing spring-webflux reactive

我正在研究一个使用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;
    }

  }
}

0 个答案:

没有答案