Testing Reactive Applications with StepVerifier in Spring WebFlux

In this post learn how to Test your reactive streams effectively in Spring WebFlux using StepVerifier from Project Reactor for reliable, non-blocking unit testing.

Testing Reactive Applications with StepVerifier in Spring WebFlux

πŸ“‘ Table of Contents:

  1. Why Testing Reactive Streams Matters
  2. Introduction to StepVerifier
  3. Setting Up Your Test Project
  4. Writing Simple Mono and Flux Tests
  5. Testing Timed Streams with Virtual Time
  6. Testing Errors and Completion
  7. Common Pitfalls
  8. Best Practices
  9. Final Thoughts

βœ… 1. Why Testing Reactive Streams Matters

In traditional applications, you can assert return values directly. But with Flux and Mono, results come asynchronously. You need tools that can verify:

  • Emitted values
  • Completion or error signals
  • Time-based behavior
  • Subscription behavior

That’s where StepVerifier comes in.

πŸ§ͺ 2. Introduction to StepVerifier

StepVerifier is a testing utility in Project Reactor designed to validate behavior of reactive streams (Mono, Flux) step-by-step.

It allows you to:

  • Assert emitted items
  • Control time with VirtualTimeScheduler
  • Expect completion or errors
  • Test backpressure (optional)

πŸ› οΈ 3. Setting Up Your Test Project

Add the required dependency:



  io.projectreactor
  reactor-test
  test


Also include JUnit 5 (or 4) as your test framework.

πŸ” 4. Writing Simple Mono and Flux Tests

Mono Test:


@Test
void testMono() {
    Mono mono = Mono.just("Hello");

    StepVerifier.create(mono)
        .expectNext("Hello")
        .expectComplete()
        .verify();
}

Flux Test:


@Test
void testFlux() {
    Flux flux = Flux.just("A", "B", "C");

    StepVerifier.create(flux)
        .expectNext("A", "B", "C")
        .expectComplete()
        .verify();
}

⏱️ 5. Testing Timed Streams with Virtual Time

This is useful for testing delayed emissions without slowing your tests.


@Test
void testFluxWithDelay() {
    StepVerifier.withVirtualTime(() -> 
        Flux.interval(Duration.ofSeconds(1)).take(3))
        .thenAwait(Duration.ofSeconds(3))
        .expectNext(0L, 1L, 2L)
        .expectComplete()
        .verify();
}

This allows testing Flux.interval without waiting real time.

❌ 6. Testing Errors and Completion

You can test errors like this:


@Test
void testFluxError() {
    Flux flux = Flux.error(new RuntimeException("Test error"));

    StepVerifier.create(flux)
        .expectErrorMessage("Test error")
        .verify();
}

⚠️ 7. Common Pitfalls

  • Don’t block with .block() or .subscribe() in tests
  • Always assert completion/error to avoid false positives
  • Use virtual time when testing interval, delayElements, or timeout

πŸ’‘ 8. Best Practices

  • Use StepVerifier.create() for simple tests
  • Prefer StepVerifier.withVirtualTime() for time-based streams
  • Test both happy path and error paths
  • Keep unit tests deterministic β€” avoid non-deterministic thread delays

βœ… 9. Final Thoughts

Testing with StepVerifier in Spring WebFlux ensures your reactive pipelines behave as expected. It’s fast, reliable, and integrates seamlessly into your testing strategy. Whether you’re emitting simple events or time-sensitive streams, StepVerifier gives you full control and visibility into reactive behavior.