In this lesson, we will cover testing with StepVerifier in Project Reactor. We will create a Publisher and then examine how it behaves. Let’s dive in.
How to test Flux and Mono?
When creating a Publisher (Flux or Mono), we would like to know how it behaves when someone subscribes to it. Well, that can be easily done with the StepVerifier class.
Using this class, we can subscribe to the Publisher, define a step-by-step test and perform some assertions. If any event is triggered that doesn’t match the current expectation, the StepVerifier will produce an AssertionError.
To be able to use the StepVerifier, we need to have the following Maven dependency in the pom.xml file:
<dependency> <groupid>io.projectreactor</groupid> <artifactid>reactor-test</artifactid> <version>3.1.0.RELEASE</version> <scope>test</scope> </dependency>
Unit Testing with StepVerifier
In the first example, let’s create a Flux of integers and specify what we expect (what elements to receive and in what order) when we subscribe.
Example
class ReactiveJavaTutorialTest { @Test public void testFlux() { //Create a Flux Flux <Integer> fluxToTest = Flux.just(1, 2, 3, 4, 5); // Create a test StepVerifier.create(fluxToTest) .expectNext(1) .expectNext(2) .expectNext(3) .expectNext(4) .expectNext(5) .expectComplete() // we expect Flux to complete after sending the number 5 which is the last element .verify(); } }
class ReactiveJavaTutorialTest { @Test public void testFlux() { //Create a Flux Flux<Integer> fluxToTest = Flux.just(1, 2, 3); // Create a test StepVerifier.create(fluxToTest) .expectNext(1) .expectNext(3) .expectNext(2) .expectComplete() .verify(); } }
class ReactiveJavaTutorialTest { @Test public void testFlux() { Flux<String> fluxToTest = Flux.just("Jessica", "John", "Tomas", "Melissa", "Steve", "Megan", "Monica", "Henry") .filter(name -> name.length() <= 5) .map(String::toUpperCase); StepVerifier.create(fluxToTest) .expectNext("JOHN") .expectNext("TOMAS") .expectNextMatches(name -> name.startsWith("ST")) .expectNext("MEGAN") .expectNext("HENRY") .expectComplete() .verify(); } }
Testing Exceptions with StepVerifier
Let’s see now how we can set the StepVerifier to expect an exception from the Flux.
For this example, we will create a Flux of integers and concatenate it with Mono that terminates immediately due to exception.
Example
class ReactiveJavaTutorialTest { @Test public void testFlux() { Flux<Integer> fluxToTest = Flux.just(1, 2, 3, 4) .concatWith(Mono.error(new ArithmeticException("Error occurred!"))); StepVerifier.create(fluxToTest) .expectNext(1) .expectNext(2) .expectNext(3) .expectNext(4) .expectErrorMatches(throwable -> throwable instanceof ArithmeticException && throwable.getMessage().equals("Error occurred!")) .verify(); } }
Testing Time Consuming Publishers
We can have Publishers that have a delay between each element that emits.
For example, if we have a Mono that will emit an element with some delay, then the StepVerifier will wait for that element until it gets it.
Example
class ReactiveJavaTutorialTest { @Test public void testFlux() { Mono<String> monoWithDelay = Mono.just("Reactive") .delayElement(Duration.ofSeconds(5)); long start = System.currentTimeMillis() / 1000; StepVerifier.create(monoWithDelay) .expectNext("Reactive") .expectComplete() .verify(); System.out.println("Time taken for the StepVerifier: " + (System.currentTimeMillis() / 1000 - start) + " seconds"); } }
Let’s expect for this Mono to emit element after only 3 seconds and see what happens:
Example
class ReactiveJavaTutorialTest { @Test public void testFlux() { Mono<String> monoWithDelay = Mono.just("Reactive") .delayElement(Duration.ofSeconds(5)); StepVerifier.create(monoWithDelay) .expectNext("Reactive") .expectComplete() .verify(Duration.ofSeconds(3)); // will wait 3 seconds } }