Overview

Reactive Programming is catching up a lot of traction lately. From Java 8’s CompletableFuture, Java 9’s Flow.Publisher, RxJava, Akka and more latest Reactor, all work on the same principle of Reactive Streams while each one has their own implementation and APIs.

We have talked about Spring WebFlux in the past which makes use of this Reactive Programming in the context of Web Application Development. Spring WebFlux by default uses Reactor project which has Flux (for 1-n data) and Mono (for 0-1 data) domain objects to return data reactively.

But testing and debugging Reactive Programming is relatively new to many. This post is intended to introduce that. Full source code is available in Github.

The service we are going to test has been written using Reactor Project which is the default Reactive framework shipped with Spring WebFlux. The service we are going to test is as below;

package lk.cms.reactive.testing.demo;

import reactor.core.publisher.Flux;

import java.time.Duration;
import java.util.Date;
import java.util.function.Function;

public class ReactiveService {

    public Flux getNumbers(int start, int count) {
        return Flux.range(start, count);
    }

    public Flux getNumbers(int start, int count, Function func) {
        return getNumbers(start, count).map(func);
    }

    public Flux getNumbers(int start, int count, Duration delay) {
        return getNumbers(start, count).delayElements(delay);
    }

    public Flux getTimeTicks() {
        return Flux.interval(Duration.ofSeconds(1)).map(tick -> new Date());
    }

}

And I will explain the StepVerifier utility class which is provided in the reactor test framework which enables to test the above service easily.

Testing

1. Testing of getNumbers method

 

    @Test
    public void getNumbers() {
        StepVerifier.create(reactiveService.getNumbers(0, 10).doOnNext(System.out::println))
                .expectSubscription()
                .expectNext(0)
                .expectNextCount(4)
                .expectNext(5, 6, 7)
                .expectNext(8)
                .expectNext(9)
                .verifyComplete();
    }

The above Test goes through numbers 0-9 while printing the numbers one by one. The usage of StepVerifier.create has enabled us to expectSubscription, expectNext (next value), expectNextCount (no of values expected), expectNext with multiple values and finally verifyComplete of the Reactive stream.

2. Testing of getNumbers_Division method

In this method we are expecting an ArithmeticException as we will be doing a divide by zero by passing a lambda as for the map function.

    @Test
    public void getNumbers_Division() {
        StepVerifier.create(Flux.zip(reactiveService.getNumbers(1, 10, (i) -> i / 0)
                                .checkpoint("division", true) // division checkpoint
                                .doOnError(t -> t.printStackTrace())
                                .doOnNext(System.out::println),
                            reactiveService.getNumbers(10, 10)
                                .checkpoint("numbers", true), (n1, n2) -> n1 + n2))
                .expectSubscription()
                .expectError(ArithmeticException.class)
                .verify();
    }

In this case we are using expectError to check for the correct exception. However here we are creating a new Reactive stream with the Flux.zip operation by zipping two separate Reactive streams. For debugging purpose we would want to know which stream produced the error. For that, we can use Flux.checkpoint method where we can give a name. So when the Exception stacktrace is printed, it will show the checkpoint name for better debugging.

3. Testing of getNumbersAndgetTimeTicks method

This test is a special case where a Delay is also involved. In this case we will run through 0-9 numbers and Time tick every one second. So the test will last 1 second * 10 = 10 seconds.

    @Test
    public void getNumbersAndgetTimeTicks() {
        StepVerifier.create(Flux.zip(reactiveService.getNumbers(0, 10, Duration.ofSeconds(1)),
                                     reactiveService.getTimeTicks(),
                                    (no, tick) -> no + ". " +tick)
                                .doOnNext(System.out::println))
                .expectSubscription()
                .expectNextCount(10)
                .verifyComplete();
    }

 

4. Testing of getNumbers_DelayElements Method

For testing of Reactive streams which involves delays as the above one, StepVerifier.withVirtualTime can be used to simulate elapsing of time instead of actually running on real time. This will make the running of test faster and no need to wait for 1 second * 10 = 10 seconds in this case.

    @Test
    public void getNumbers_DelayElements() {
        StepVerifier.withVirtualTime(() -> reactiveService.getNumbers(0, 10, Duration.ofSeconds(1))
                                                                .doOnNext(System.out::println))
                .expectSubscription()
                .expectNext(0)
                .thenAwait(Duration.ofSeconds(1))
                .expectNext(1, 2, 3, 4, 5, 6, 7, 8, 9)
                .verifyComplete();
    }

Original Post : https://www.techtalks.lk/blog/2018/3/testing-and-debugging-reactor

Author : Admin
Published Date March 13, 2018
Share Post