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.

π Table of Contents:
- Why Testing Reactive Streams Matters
- Introduction to StepVerifier
- Setting Up Your Test Project
- Writing Simple Mono and Flux Tests
- Testing Timed Streams with Virtual Time
- Testing Errors and Completion
- Common Pitfalls
- Best Practices
- 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:
1 2 3 4 5 6 7 8 |
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> |
Also include JUnit 5 (or 4) as your test framework.
π 4. Writing Simple Mono and Flux Tests
Mono Test:
1 2 3 4 5 6 7 8 9 10 11 12 |
@Test void testMono() { Mono<String> mono = Mono.just("Hello"); StepVerifier.create(mono) .expectNext("Hello") .expectComplete() .verify(); } |
Flux Test:
1 2 3 4 5 6 7 8 9 10 11 12 |
@Test void testFlux() { Flux<String> 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.
1 2 3 4 5 6 7 8 9 10 11 12 |
@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:
1 2 3 4 5 6 7 8 9 10 11 |
@Test void testFluxError() { Flux<String> 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
, ortimeout
π‘ 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.