7

I am working on a project that uses the Spring WebFlux stack. We have a Controller where you can subscribe for updates on a specific object. This Controller returns an EmitterProcessor where clients can subscribe to. When something is published on the EmitterProcessor, subscribed clients get notified.

This works well in practice, but my unit test is failing. The unit test uses the WebTestClient, which blocks when an exchange() operation returns a EmitterProcessor (also tried with other FluxProcessor implementations such as UnicastProcessor). The error I get is the following:

java.lang.IllegalStateException: Timeout on blocking read for 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)

I found this thread, which also reports that the exchange() method on the WebTestClient blocks, but as explained there it blocks only for retrieving the status and headers, so this should not be the problem. Also, in the case that a Flux is returned, this works fine, as demonstrated by the referenced testcase.

Test case

I created a simple test case derived from the referenced test case and adapted it to return a Flux in one case and an EmitterProcessor in another case (which fails). As you might notice, the assertions for the EmitterProcessor should fail it, but it never gets there because of the blocking exchange() call.

Also notice that when I uncomment the line processor.onNext("hello");, this content is returned and the testcase succeeds.

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;
    }

  }
}
Geert Graat
  • 145
  • 3
  • 10
  • Did you solve it ? – Amir Choubani Nov 05 '21 at 11:20
  • 1
    Unfortunately not. We ran into some other problems with Spring WebFlux and abandoned it. I have not followed developments in this area, but I would not be surprised if this has been fixed, or alternatives are in place. I know for instance that EmitterProcessor is deprecated now. – Geert Graat Nov 09 '21 at 08:16

0 Answers0