Search code examples
javaspringspring-bootspring-webfluxspock

Looks like my test doesn't wait for subscription - webflux, spock, stepVerifier


I simplified my problem to this pseudo code. My controller

@RestController
@RequestMapping("api")
@RequiredArgsConstructor
public class TestController {
  private final Service service;
  @PostMapping("/test")
  public Mono<Void> test(@RequestBody @Valid Flux<TestDto> testDtoFlux) {
    return testDtoFlux
      .map(service::test)
      .then();
  }

test:

  void setup() {
    service = Mock(Service)
    controller = new TestController(service)
  }

  def "Should test abc"() {
    setup:
        def testDto = new TestDto(11L, true)

    when:
        def result = StepVerifier.create(controller.test(Flux.just(testDto)))

    then:
        result
          .verifyComplete()

        1 * service.test(_) >> true
  }

test everytime says that service.test is never executed but when I changed code to something like this test passed:

  public Mono<Void> test(@RequestBody @Valid Flux<TestDto> testDtoFlux) {
    testDtoFlux
      .map(service::test)
      .then().block();
    return Mono.empty();
  }

I'm not sure why this is happening. I was trying to fix that with for example that:

result
  .expectSubscription()
  .thenAwait(Duration.ofSeconds(5))
  .expectComplete()
  .verify();

but result is always the same. Maybe you can see what I'm doing wrong?

ps. When I run the application, everything works fine. I only have a problem with this test.


Solution

  • The problem is that all actions must be completed inside the when block. While result.verifyComplete() may look like a simple verification, it is actually what triggers the subscription to the Mono in this case.

    Now, this clashes with one of Spock's idiosyncrasies, namely that all interactions in a then block are asserted first regardless of whatever order you write other checks in that block.

    So what actually happens is that

    1. You prepare the Mono in the when block, but not action is performed
    2. The mock interaction is verified, and fail because nothing happened yet
    3. (the stepverifier would subscribe and trigger the action and verify the result)

    To fix the problem, it should be written like this:

      def "Should test abc"() {
        setup:
            def testDto = new TestDto(11L, true)
    
        when:
            StepVerifier.create(controller.test(Flux.just(testDto)))
              .verifyComplete()
    
        then:
            1 * service.test(_) >> true
      }