[Spring-Reactive] Reactor (Testing)

StepVerifier를 이용한 Testing 1

  • Flux 또는 Mono로 선언된 Operator 체인을 구독 시, 동작 방식을 테스트하기 위한 가장 일반적인 테스트 방식
  • expectXXXX()를 이용해서 Sequence 상에서 예상되는 Signal의 기대값을 assertion할 수 있다.
  • verify() Operator를 호출함으로써 전체 Operator 체인의 테스트를 트리거 한다.
  • verifyXXXX()를 이용해서 전체 Operator 체인의 테스트를 트리거 + 종료 또는 에러 이벤트 검증을 수행할 수 있다.
  • 실제 수행되는 시간과 관련된 테스트를 수행할 수 있다.

GeneralExample Class

  • 테스트시 필요한 클래스
import reactor.core.publisher.Flux;

public class GeneralExample {
    public static Flux<String> sayHelloReactor() {
        return Flux
                .just("Hello", "Reactor");
    }

    public static Flux<Integer> divideByTwo(Flux<Integer> source) {
        return source
                .zipWith(Flux.just(2, 2, 2, 2, 2), (x, y) -> x/y);
    }

    public static Flux<Integer> occurError(Flux<Integer> source) {
        return source
                .zipWith(Flux.just(2, 2, 2, 2, 0), (x, y) -> x/y);
    }

    public static Flux<Integer> takeNumber(Flux<Integer> source, long n) {
        return source
                .take(n);
    }
}

TimeBasedExample Class

  • 테스트시 필요한 클래스
public class TimeBasedExample {
    public static Flux<Tuple2<String, Integer>> getCOVID19Count(Flux<Long> source) {
        return source
                .flatMap(notUse -> Flux.just(
                                        Tuples.of("서울", 1000),
                                        Tuples.of("경기도", 500),
                                        Tuples.of("강원도", 300),
                                        Tuples.of("충청도", 60),
                                        Tuples.of("경상도", 100),
                                        Tuples.of("전라도", 80),
                                        Tuples.of("인천", 200),
                                        Tuples.of("대전", 50),
                                        Tuples.of("대구", 60),
                                        Tuples.of("부산", 30),
                                        Tuples.of("제주도", 5)
                                    )
                );
    }

    public static Flux<Tuple2<String, Integer>> getVoteCount(Flux<Long> source) {
        return source
                .zipWith(Flux.just(
                                    Tuples.of("중구", 15400),
                                    Tuples.of("서초구", 20020),
                                    Tuples.of("강서구", 32040),
                                    Tuples.of("강동구", 14506),
                                    Tuples.of("서대문구", 35650)
                                )
                )
                .map(Tuple2::getT2);
    }
}

Test

  • 테스트 directory
import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;

/***
 * expectNext()를 사용하여 emit 된 n 개의 데이터를 검증하는 예제
 * 결과는 성공
 */
public class StepVerifierGeneralTestExample02 {
    @Test
    public void sayHelloReactorTest() {
        StepVerifier
                .create(GeneralExample.sayHelloReactor())
                .expectSubscription()
                .expectNext("Hello")
                .expectNext("Reactor")
                .expectComplete()
                .verify();
    }
}
import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;

/**
 * - verifyComplete()을 사용하여 검증 실행 및 기대값으로 onComplete signal 이 맞는지 검증하는 예제
 *  - as(description)를 사용해서 실패한 expectXXXX()에게 description 을 지정할 수 있다.
 *  결과 : Hi에서 실패를 하게 되며 '# expect Hi' 의 값이 출력된다.
 */
public class StepVerifierGeneralTestExample03 {
    @Test
    public void sayHelloReactorTest() {
        StepVerifier
                .create(GeneralExample.sayHelloReactor())
                .expectSubscription()
                .as("# expect subscription")
                .expectNext("Hi")
                .as("# expect Hi")
                .expectNext("Reactor")
                .as("# expect Reactor")
                .verifyComplete();
    }
}

img_9.png

import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

/**
 * onError signal 발생 여부를 검증
 * 결과 : 10을 0으로 나눌 수 업기 때문에 에러가 나며, expectError 에서 검증되며 성공 리턴.
 */
public class StepVerifierGeneralTestExample04 {
    @Test
    public void occurErrorTest() {
        Flux<Integer> source = Flux.just(2, 4, 6, 8, 10);
        StepVerifier
                .create(GeneralExample.occurError(source))
                .expectSubscription()
                .expectNext(1)
                .expectNext(2)
                .expectNext(3)
                .expectNext(4)
                .expectError()
                .verify();
    }
}
import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

/**
 * 1개 이상의 emit 된 데이터를 한꺼번에 검증
 * 결과 : 성공
 */
public class StepVerifierGeneralTestExample05 {
    @Test
    public void divideByTwoTest() {
        Flux<Integer> source = Flux.just(2, 4, 6, 8, 10);
        StepVerifier
                .create(GeneralExample.divideByTwo(source))
                .expectSubscription()
                .expectNext(1, 2, 3, 4, 5)
                .expectComplete()
                .verify();
    }
}
import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import reactor.test.StepVerifierOptions;

/**
 * onNext signal 을 통해 emit 된 데이터의 개수를 검증하는 예제
 *  - 검증에 실패할 경우에는 StepVerifierOptions에서 지정한 Scenario Name이 표시된다.
 */
public class StepVerifierGeneralTestExample06 {
    @Test
    public void rangeNumberTest() {
        Flux<Integer> source = Flux.range(0, 1000);
        StepVerifier
                .create(GeneralExample.takeNumber(source, 500),
                        StepVerifierOptions.create().scenarioName("Verify from 0 to 499"))
                .expectSubscription()
                .expectNext(0)
                .expectNextCount(498)
                .expectNext(499)
                .expectComplete()
                .verify();
    }
}

img_10.png

import com.itvillage.section10.class00.TimeBasedExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import reactor.test.scheduler.VirtualTimeScheduler;

import java.time.Duration;

/**
 * 실제 시간을 가상 시간으로 대체하는 테스트 예제
 *  - 특정 시간만큼 시간을 앞당긴다.
 *  참고 : 여기서 take(1) 는 interval을 1번만 한다는 뜻, 예제 기준으로 12시간 후에 이벤트 한번만 실행, 이후엔 실행 안한다.
 */
public class StepVerifierTimeBasedTestExample01 {
    @Test
    public void getCOVID19CountTest() {
        StepVerifier
                .withVirtualTime(() -> TimeBasedExample.getCOVID19Count(
                        Flux.interval(Duration.ofHours(12)).take(1)
                    )
                )
                .expectSubscription()
                .then(() -> VirtualTimeScheduler.get().advanceTimeBy(Duration.ofHours(12)))
                .expectNextCount(11)
                .expectComplete()
                .verify();

    }
}
/**
 * 실제 시간을 가상 시간으로 대체하는 테스트 예제
 *  - thenAwait(Duration)을 통해 특정 시간만큼 가상으로 기다린다.
 *  - 즉, 특정 시간을 기다린 것처럼 시간을 당긴다.
 */
public class StepVerifierTimeBasedTestExample02 {
    @Test
    public void getCOVID19CountTest() {
        StepVerifier
                .withVirtualTime(() -> TimeBasedExample.getCOVID19Count(
                        Flux.interval(Duration.ofHours(12)).take(1)
                    )
                )
                .expectSubscription()
                .thenAwait(Duration.ofHours(12))
                .expectNextCount(11)
                .expectComplete()
                .verify();

    }
}
import com.itvillage.section10.class00.TimeBasedExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

import java.time.Duration;

/**
 * 검증에 소요되는 시간을 제한하는 예제
 *  - verify(Duration)을 통해 설정한 시간내에 검증이 끝나는지를 확인할 수 있다.
 *  - verify(Duration)에 설정한 시간까지 실행한다.
 */
public class StepVerifierTimeBasedTestExample03 {
    @Test
    public void getCOVID19CountTest() {
        StepVerifier
                .create(TimeBasedExample.getCOVID19Count(
                        Flux.interval(Duration.ofMinutes(1)).take(1)
                    )
                )
                .expectSubscription()
                .expectNextCount(11)
                .expectComplete()
                .verify(Duration.ofSeconds(3));
    }
}
/**
 * expectNoEvent(Duration)으로 지정된 대기 시간동안 이벤트가 없을을 확인하는 예제
 */
public class StepVerifierTimeBasedTestExample04 {
    @Test
    public void getCOVID19CountTest() {
        StepVerifier
                .withVirtualTime(() -> TimeBasedExample.getVoteCount(
                        Flux.interval(Duration.ofMinutes(1))
                    )
                )
                .expectSubscription()
                .expectNoEvent(Duration.ofMinutes(1))
                .expectNext(Tuples.of("중구", 15400))
                .expectNoEvent(Duration.ofMinutes(1))
                .expectNoEvent(Duration.ofMinutes(1))
                .expectNoEvent(Duration.ofMinutes(1))
                .expectNoEvent(Duration.ofMinutes(1))
                .expectNextCount(4)
                .expectComplete()
                .verify();

    }
}

StepVerifier를 이용한 Testing 2

  • Backpressure 테스트
    • hasDropped(), hasDiscarded() 등을 이용해서 backpressure 테스트를 수행할 수 있다.
  • Context 테스트
    • expectAccessibleContext()를 이용해서 접근 가능한 Context가 있는지 테스트 할 수 있다.
    • hasKey()를 사용하여 Context의 key가 존재하는지 검증할 수 있다.
  • 기록된 데이터를 이용한 테스트
    • recordWith()를 사용하여 emit 된 데이터를 기록할 수 있다.
    • consumeRecordedWith()를 사용하여 기록된 데이터들을 소비하며 검증할 수 있다.
    • expectRecordedMatches()를 사용하여 기록된 데이터의 컬렉션을 검증할 수 있다.

BackpressureExample


import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;

public class BackpressureExample {
    public static Flux<Integer> generateNumberByErrorStrategy() {
        return Flux
                .create(emitter -> {
                    for (int i = 1; i <= 100; i++) {
                        emitter.next(i);
                    }
                    emitter.complete();
                }, FluxSink.OverflowStrategy.ERROR);
    }

    public static Flux<Integer> generateNumberByDropStrategy() {
        return Flux
                .create(emitter -> {
                    for (int i = 1; i <= 100; i++) {
                        emitter.next(i);
                    }
                    emitter.complete();
                }, FluxSink.OverflowStrategy.DROP);
    }
}

ContextExample


import reactor.core.publisher.Mono;

public class ContextExample {
    public static Mono<String> helloMessage(Mono<String> source, String key) {
        return source
                .zipWith(Mono.deferContextual(ctx -> Mono.just(ctx.get(key)))) // 두 개의 Mono 스트림을 결합하여 **튜플(Tuple2)**로 반환
                .map(tuple -> tuple.getT1() + ", " + tuple.getT2());

    }
}

RecordExample

import reactor.core.publisher.Flux;

public class RecordExample {
    public static Flux<String> getCountry(Flux<String> source) {
        return source
                .map(country -> country.substring(0, 1).toUpperCase() + country.substring(1));
    }
}

Test

BackpressureTest

  • Discarded: 스트림 완료 또는 종료 시, 버퍼에 있던 데이터가 의도적으로 버려진 경우.
  • Dropped: 백프레셔나 오버플로우로 인해 처리되지 않고 무시된 데이터.
import com.itvillage.section10.class01.BackpressureExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;

/**
 * Backpressure 전략에 따라 Exception이 발생하는 예제
 *  - request 데이터 개수보다 많은 데이터가 emit 되어 OverFlowException이 발생
 *  - OverFlowException이 발생하게 된 데이터는 discard 된다.
 *  - 나머지 emit 된 데이터들은 Hooks.onNextDropped()에 의해 drop된다.
 *  - StepVerifier.create(Flux, 1L)는 구독 시 최대 1개만 요청되야 하지만 100개가 요청되어 실패 된다.
 *  - thenConsumeWhile 는 소비되는 데이터가 주어진 조건에 맞아야 성공.
 */
public class StepVerifierBackpressureTestExample01 {
    @Test
    public void generateNumberTest() {
        StepVerifier
                .create(BackpressureExample.generateNumberByErrorStrategy(), 1L)
                .thenConsumeWhile(num -> num >= 1) // emit 된 데이터들을 소비한다.
                .verifyComplete();
    }
}
import com.itvillage.section10.class01.BackpressureExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;

/**
 * Backpressure ERROR 전략을 검증하는 예제
 *  - expectError()를 사용하여 에러가 발생되었는지 검증
 *  - verifyThenAssertThat()을 사용하여 검증 이후에 assertion method 를 사용하여 추가 검증을 할 수 있다.
 *  - hasDiscardedElements()를 사용하여 discard된 데이터가 있는지 검증한다. OverflowException이 발생할 때 2가 discard된다.
 *  - hasDiscarded()를 사용하여 discard된 데이터가 무엇인지 검증한다. OverflowException이 발생할 때 2가 discard된다.
 *  - hasDroppedElements()을 사용하여 Hooks.onNextDropped()으로 Drop된 데이터가 있는지를 검증한다.
 *  - hasDropped()를 사용하여 Hooks.onNextDropped()으로 Drop된 데이터가 무엇인지 검증한다.
 *  - hasDiscarded : 버퍼에 잠깐 들어갔지만, 요청이 없어서 사용되지 않고 버려진 데이터(2)
 *  - hasDropped : 버퍼에 들어갈 자리조차 없어서 처음부터 무시된 데이터(3, 4, 5, ...).
 */
public class StepVerifierBackpressureTestExample02 {
    @Test
    public void generateNumberTest() {
        StepVerifier
                .create(BackpressureExample.generateNumberByErrorStrategy(), 1L)
                .thenConsumeWhile(num -> num >= 1)
                .expectError()
                .verifyThenAssertThat()
                .hasDiscardedElements()
                .hasDiscarded(2)
                .hasDroppedElements()
                .hasDropped(3, 4, 5, 6, 98, 99, 100);
    }
}
import com.itvillage.section10.class01.BackpressureExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;

/**
 * Backpressure DROP 전략을 검증하는 예제
 *  - expectError()를 사용하여 에러가 발생되었는지 검증
 *  - verifyThenAssertThat()을 사용하여 검증 이후에 assertion method 를 사용하여 추가 검증을 할 수 있다.
 *  - hasDiscardedElements()를 사용하여 discard된 데이터가 있는지를 검증한다. Backpressure DROP 전략은 Drop된 데이터가 discard된다.
 *  - hasDiscarded()를 사용하여 discard된 데이터가 무엇인지 검증한다. Backpressure DROP 전략은 Drop된 데이터가 discard된다.
 */
public class StepVerifierBackpressureTestExample03 {
    @Test
    public void generateNumberTest() {
        StepVerifier
                .create(BackpressureExample.generateNumberByDropStrategy(), 1L)
                .thenConsumeWhile(num -> num >= 1)
                .expectComplete()
                .verifyThenAssertThat()
                .hasDiscardedElements()
                .hasDiscarded(2, 3, 4, 5, 6, 98, 99, 100);
//                .hasDropped(2, 3, 4, 5, 6, 98, 99, 100);
    }
}

RecordTest

import com.itvillage.section10.class01.RecordExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

import java.util.ArrayList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasLength;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.Every.everyItem;

/**
 * emit 되는 모든 데이터들을 캡쳐하여 컬렉션에 기록한 후, 기록된 데이터들을 검증하는 예제
 *  - recordWith()를 사용하여 emit 된 데이터를 기록하는 세션을 시작한다.
 *  - thenConsumeWhile()을 사용하여 조건에 맞는 데이터만 소비한다. 여기서 조건에 맞는 데이터들이 ArrayList 에 추가(기록)된다.
 *  - consumeRecordedWith()를 사용하여 기록된 데이터들을 소비한다. 여기서는 assertThat()을 사용하여 검증한다.
 */
public class StepVerifierRecordTestExample01 {
    @Test
    public void getCountryTest() {
        StepVerifier
                .create(RecordExample.getCountry(Flux.just("france", "russia", "greece", "poland")))
                .expectSubscription()
                .recordWith(ArrayList::new) // 소비된 데이터를 기록할 컬렉션(ArrayList) 준비
                .thenConsumeWhile(country -> !country.isEmpty()) // country 스트림을 소비, 빈 문자열이 나오면 중단
                // 기록된 데이터(countries)에 대한 추가 검증
                .consumeRecordedWith(countries -> {
                    assertThat(countries, everyItem(hasLength(6)));
                    assertThat(
                            countries
                                    .stream()
                                    .allMatch(country -> Character.isUpperCase(country.charAt(0))),
                            is(true)
                    );
                })
                .expectComplete()
                .verify();
    }
}
import com.itvillage.section10.class01.RecordExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

import java.util.ArrayList;

/**
 * emit 되는 모든 데이터들을 캡쳐하여 컬렉션에 기록한 후, 기록된 데이터들을 검증하는 예제
 *  - recordWith()를 사용하여 emit 된 데이터를 기록하는 세션을 시작한다.
 *  - thenConsumeWhile()을 사용하여 조건에 맞는 데이터만 소비한다. 여기서 조건에 맞는 데이터들이 ArrayList 에 추가(기록)된다.
 *  - expectRecordedMatches()를 사용하여 기록된 데이터의 컬렉션을 검증한다.
 */
public class StepVerifierRecordTestExample02 {
    @Test
    public void getCountryTest() {
        StepVerifier
                .create(RecordExample.getCountry(Flux.just("france", "russia", "greece", "poland")))
                .expectSubscription()
                .recordWith(ArrayList::new)
                .thenConsumeWhile(country -> !country.isEmpty())
                .expectRecordedMatches(countries ->
                        countries
                                .stream()
                                .allMatch(country ->
                                        Character.isUpperCase(country.charAt(0))))
                .expectComplete()
                .verify();
    }
}

ContextTest

import com.itvillage.section10.class01.ContextExample;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

/**
 * Reactor Sequence 에서 사용되는 Context 를 검증하는 예제
 *  - expectAccessibleContext()을 사용하여 접근 가능한 Context가 있는지를 검증한다.
 *  - hasKey()를 사용하여 Context의 key가 존재하는지 검증한다.
 *  - then()을 사용하여 검증을 위한 후속 작업을 진행할 수 있다.
 */
public class StepVerifierContextTestExample01 {
    final private static String KEY = "helloTarget";

    @Test
    public void helloMessageTest() {
        Mono<String> source = Mono.just("Hello");

        StepVerifier
                .create(ContextExample
                        .helloMessage(source, KEY)
                        .contextWrite(context -> context.put(KEY, "Reactor"))
                )
                .expectSubscription()
                .expectAccessibleContext()
                .hasKey("helloTarget")
                .then()
                .expectNext("Hello, Reactor")
                .expectComplete()
                .verify();
    }
}

TestPublisher를 이용한 Testing

  • Testing 목적에 사용하기 위한 Publisher이다.
  • 개발자가 직접 프로그래밍을 통해 signal을 발생시킬 수 있다.
  • 주로 특정한 상황을 재현하여 테스트하고 싶은 경우 사용할 수 있다.
  • 리액티브 스트림즈 사양을 준수하는지의 여부를 테스트할 수 있다.
import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;

/**
 * TestPublisher 를 사용해서 서비스 로직의 메서드에 대한 Unit Test 를 실시하는 예제
 *  - 정상 동작하는 TestPublisher
 *  - next() 사용
 */
public class TestPublisherTestExample01 {
    @Test
    public void divideByTwoTest() {
        TestPublisher<Integer> source = TestPublisher.create();

        StepVerifier
                .create(GeneralExample.divideByTwo(source.flux())) // 테스트퍼블리셔를 받고 있다.
                .expectSubscription()
                .then(() -> source.next(2, 4, 6, 8, 10)) // 데이터 emit
                .expectNext(1, 2, 3, 4, 5)
                .expectComplete()
                .verify();
    }
}
import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;

/**
 * TestPublisher 를 사용해서 서비스 로직의 메서드에 대한 Unit Test 를 실시하는 예제
 *  - 정상 동작하는 TestPublisher
 *  - next() 사용
 *  - 에러 발생 여부 검증
 */
public class TestPublisherTestExample02 {
    @Test
    public void divideByTwoTest() {
        TestPublisher<Integer> source = TestPublisher.create();

        StepVerifier
                .create(GeneralExample.occurError(source.flux()))
                .expectSubscription()
                .then(() -> source.next(2, 4, 6, 8, 10))
                .expectNext(1)
                .expectNext(2)
                .expectNext(3)
                .expectNext(4)
                .expectError()
                .verify();
    }
}
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;

/**
 * TestPublisher 를 사용해서 서비스 로직의 메서드에 대한 Unit Test 를 실시하는 예제
 *  - 정상 동작하는 TestPublisher
 *  - emit() 사용
 */
public class TestPublisherTestExample03 {
    @Test
    public void divideByTwoTest() {
        TestPublisher<Integer> source = TestPublisher.create();

        StepVerifier
                .create(source.flux())
                .expectSubscription()
                .then(() -> source.emit(1, 2, 3))
                .expectNext(1)
                .expectNext(2)
                .expectNext(3)
                .expectComplete()
                .verify();
    }
}
import com.itvillage.section10.class00.GeneralExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;

/**
 * TestPublisher 를 사용해서 서비스 로직의 메서드에 대한 Unit Test 를 실시하는 예제
 *  - Reactive Streams 사양을 위반해도 Publisher가 정상 동작하게 함으로써 서비스 로직을 검증하는 예제
 *  - 에러의 내용이 다르다. (null 입력이 안됨 vs null 입력은 되지만 이후 에러)
 */
public class TestPublisherTestExample04 {
    @Test
    public void divideByTwoTest() {
        TestPublisher<Integer> source = TestPublisher.createNoncompliant(TestPublisher.Violation.ALLOW_NULL); // 사양을 준수 하지 않는 방법
//        TestPublisher<Integer> source = TestPublisher.create();

        StepVerifier
                .create(GeneralExample.divideByTwo(source.flux()))
                .expectSubscription()
                .then(() -> source.next(2, 4, 6, 8, null))
                .expectNext(1)
                .expectNext(2)
                .expectNext(3)
                .expectNext(4)
                .expectComplete()
                .verify();
    }
}

PublisherProbe를 이용한 Testing

  • Operator 체인의 실행 경로를 검증할 수 있다.
  • 주로 조건에 따른 분기로 인해서 Sequence가 분기 되는 경우, 실행 경로를 추적해서 정상적으로 실행이 되었는지 확인할 수 있다.
  • 해당 실행 경로대로 정상적으로 실행되었는지의 여부는 assertWasSubscribed(), assertWasRequested(), assertWasCancelled() 를 통해 검증할 수 있다.

class

package com.itvillage.section10.class02;

import reactor.core.publisher.Mono;

public class PublisherProbeExample {
    public static Mono<String> processWith(Mono<String> main, Mono<String> standby) {
        return main
                .flatMap(massage -> Mono.just(massage))
                .switchIfEmpty(standby);
    }

    public static Mono<String> useMainPower() {
        return Mono.empty();
    }

    public static Mono useStandbyPower() {
        return Mono.just("# use Standby Power");
    }
}

Test

import com.itvillage.section10.class02.PublisherProbeExample;
import org.junit.jupiter.api.Test;
import reactor.test.StepVerifier;
import reactor.test.publisher.PublisherProbe;

public class PublisherProbeTestExample01 {
    @Test
    public void publisherProbeTest() {
        PublisherProbe<String> probe = PublisherProbe.of(PublisherProbeExample.useStandbyPower());

        StepVerifier
                .create(PublisherProbeExample.processWith(PublisherProbeExample.useMainPower(), probe.mono()))
                .expectNextCount(1)
                .verifyComplete();

        // 실행 경로 대로 실행 되었는지 확인(순서는 상관 없음)
        probe.assertWasSubscribed(); // 구독 확인
        probe.assertWasRequested(); // 데이터 요청 확인
        probe.assertWasNotCancelled(); // 구독 취소가 없었는지 확인
    }
}

© 2023 Lee. All rights reserved.