1.概要

このチュートリアルでは、http://www.reactive-streams.org/[reactive streams]を

StepVerifier



TestPublisher

でテストする方法について詳しく説明します。

私たちは、一連の原子炉運転を含むhttps://www.baeldung.com/spring-reactor[

Spring Reactor

]アプリケーションに基づいて調査を進めます。

2. Mavenの依存関係

Spring Reactorは反応性ストリームをテストするためのいくつかのクラスが付属しています。

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <scope>test</scope>
    <version>3.2.3.RELEASE</version>
</dependency>

3.

StepVerifier

一般に、

reactor-test

には主に2つの用途があります。


  • StepVerifier

    を使った段階的なテストの作成

  • ____TestPublisherを使った定義済みデータの生成

演算子

リアクティブストリームをテストする際の最も一般的なケースは、コードにパブリッシャー(


_Flux


または

Mono_

)が定義されている場合です。

誰かが購読したときの動作を知りたい。


StepVerifier

APIを使用すると、

どの要素が期待され、ストリームが完了したときに何が起こるか

という点で、公開要素に対する期待を定義できます。

まず最初に、何人かの運営者で出版社を作りましょう。

私たちは__Flux.just(T elements)を使います。

高度な演算子はこの記事の範囲を超えているため、大文字にマッピングされた4文字の名前のみを出力する単純な媒体社を作成します。

Flux<String> source = Flux.just("John", "Monica", "Mark", "Cloe", "Frank", "Casper", "Olivia", "Emily", "Cate")
  .filter(name -> name.length() == 4)
  .map(String::toUpperCase);

3.1. ステップバイステップのシナリオ

それでは、誰かが購読したときに何が起こるかをテストするために、


source


step

Verifier

** をテストしましょう。

StepVerifier
  .create(source)
  .expectNext("JOHN")
  .expectNextMatches(name -> name.startsWith("MA"))
  .expectNext("CLOE", "CATE")
  .expectComplete()
  .verify();

まず、

__ create


methodを使って


StepVerifier

__builderを作成します。

次に、テスト中の

__Flux


sourceをラップします。最初のシグナルは


expectNext(T element)で検証されます、実際には**

expectNext __には任意の数の要素を渡すことができます。

また、

__expectNextMatches


を使用して、よりカスタムマッチングのための


Predicate <T>

__を提供することもできます。

私たちの最後の期待のために、私たちは私たちの流れが完成することを期待します。

そして最後に、


verify()

を使ってテストを起動します

3.2.

StepVerifier

での例外

それでは、私たちの

Flux

パブリッシャーと__Monoを連結しましょう。

  • これを購読するとエラーが発生します。

Flux<String> error = source.concatWith(
  Mono.error(new IllegalArgumentException("Our message"))
);

さて、4つのすべての要素の後、

ストリームは例外で終了すると予想します

StepVerifier
  .create(error)
  .expectNextCount(4)
  .expectErrorMatches(throwable -> throwable instanceof IllegalArgumentException &&
    throwable.getMessage().equals("Our message")
  ).verify();

  • OnError__シグナルは、パブリッシャがエラー状態でクローズされたことをサブスクライバに通知します。そのため、後でこれ以上期待することはできません** 。

例外の種類とメッセージを一度に確認する必要がない場合は、専用の方法のいずれかを使用できます。


  • expectError()

    – あらゆる種類のエラーを予期する


  • expectError(クラス<?Throwable>

    clazz____) – エラーを期待する

特定のタイプの
**

__expectErrorMessage(String errorMessage) – エラーが発生した場合は

__expect

特定のメッセージ
**

expectErrorMatches(Predicate <Throwable> predicate)

– エラーを想定

与えられた述語にマッチする
**

expectErrorSatisfies(コンシューマ<Throwable> assertionConsumer)

カスタムアサーションを実行するために

__Throwable

__を消費する

3.3. 時間ベースの発行者のテスト

  • 時に私たちの出版社は時間ベースです。**

たとえば、実際のアプリケーションで、** イベント間に1日の遅れがあるとします。さて、当然のことながら、予想される動作をこのような遅れで検証するために、テストを1日中実行したくないということです。


  • StepVerifier.withVirtualTime

    builderは長時間のテストを避けるように設計されています。


  • withVirtualTime

    を呼び出してビルダーを作成します。


    このメソッドは


    Flux


    入力を必要としません** 代わりに

    Supplier

    を受け取ります。

イベント間の予想遅延をテストする方法を説明するために、2秒間実行される1秒の間隔で

__Flux

__を作成しましょう。 ** タイマーが正しく動作するなら、2つの要素しか得られないはずです。

StepVerifier
  .withVirtualTime(() -> Flux.interval(Duration.ofSeconds(1)).take(2))
  .expectSubscription()
  .expectNoEvent(Duration.ofSeconds(1))
  .expectNext(0L)
  .thenAwait(Duration.ofSeconds(1))
  .expectNext(1L)
  .verifyComplete();

コードで

__Flux


earlierをインスタンス化してから、この変数を


Supplier


returningすることは避けてください。代わりに、** ラムダの内側に


Flux

__を常にインスタンス化する必要があります。

時間を扱う2つの主要な期待方法があります。


  • thenAwait(Duration duration) –

    はステップの評価を一時停止します。

この間に新しいイベントが発生する可能性があります。
** ____expectNoEvent(Duration duration) – 何らかのイベントが発生したときに失敗

期間中シーケンスは与えられた

duration

で渡されます

最初のシグナルはサブスクリプションイベントなので、


すべての

expectNoEvent(Duration duration)

の前に



__

expectSubscription()

.

__を付ける必要があります。

3.4.

StepVerifier

を使用した実行後アサーション

したがって、これまで見てきたように、私たちの期待を段階的に説明するのは簡単です。

ただし、

シナリオ全体が正しく実行された後で、

追加の状態を確認する必要がある場合があります

カスタムパブリッシャーを作成しましょう。 ** それはいくつかの要素を放出し、それから完成し、一時停止し、そしてもう一つの要素を放出します。

Flux<Integer> source = Flux.<Integer>create(emitter -> {
    emitter.next(1);
    emitter.next(2);
    emitter.next(3);
    emitter.complete();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    emitter.next(4);
}).filter(number -> number % 2 == 0);

私たちは最初に

emitter.complete

を呼び出したので、それは2を出すが4を落とすことを期待しています。

それで、

__verifyThenAssertThatを使ってこの振る舞いを検証しましょう。


このメソッドは


StepVerifier.Assertions

__onを返します。これにアサーションを追加できます。

@Test
public void droppedElements() {
    StepVerifier.create(source)
      .expectNext(2)
      .expectComplete()
      .verifyThenAssertThat()
      .hasDropped(4)
      .tookLessThan(Duration.ofMillis(1050));
}

4.

TestPublisher

を使ってデータを生成する

時々、私達は選ばれたシグナルを引き起こすためにある特別なデータを必要とするかもしれません。

たとえば、テストしたい特定の状況があるかもしれません。

あるいは、独自の演算子を実装し、それがどのように動作するかをテストしたい場合があります。

どちらの場合も、

TestPublisher <T>

を使用することができます。これにより、プログラム的にその他のシグナルをトリガーすることができます。


  • next(T値)

    または____next(T値、T rest) – 1を送信または

加入者へのより多くの信号
**

__emit(T値) –


next(T)と同じ

__but

その後

complete()

を呼び出します
**

complete()



complete

シグナルでソースを終了します



  • _error(Throwable tr) – エラーでソースを終了する

    _



  • _flux() –


    TestPublisherをラップする便利なメソッド


    into

    Flux_


  • mono()

    –同じ私たち

    __flux()


    ただし

    Mono__にラップ

4.1.

TestPublisher

を作成する

簡単な____TestPublisherを作成しましょう。それは、いくつかのシグナルを発し、そして例外で終了します:

TestPublisher
  .<String>create()
  .next("First", "Second", "Third")
  .error(new RuntimeException("Message"));

4.2.

TestPublisher

の動作

前述したように、特定の状況に厳密に一致する、細かく選択されたシグナルをトリガーしたい場合があります。

さて、この場合、データのソースについて完全に習得していることが特に重要です。これを達成するために、我々は再び

TestPublisher

に頼ることができます。

まず、

getUpperCase()

操作を実行するためのコンストラクタパラメータとして____Flux <String>を使用するクラスを作成しましょう。

class UppercaseConverter {
    private final Flux<String> source;

    UppercaseConverter(Flux<String> source) {
        this.source = source;
    }

    Flux<String> getUpperCase() {
        return source
          .map(String::toUpperCase);
    }
}



_UppercaseConverter


が複雑なロジックと演算子を持つクラスであり、


source

_

publisherから非常に特定のデータを提供する必要があるとします。

__TestPublisherを使えば簡単に実現できます。

final TestPublisher<String> testPublisher = TestPublisher.create();

UppercaseConverter uppercaseConverter = new UppercaseConverter(testPublisher.flux());

StepVerifier.create(uppercaseConverter.getUpperCase())
  .then(() -> testPublisher.emit("aA", "bb", "ccc"))
  .expectNext("AA", "BB", "CCC")
  .verifyComplete();

この例では、

__UppercaseConverter


constructorパラメーターにテスト


Flux


publisherを作成します。次に、

TestPublisher__が3つの要素を発行して完了します。

4.3. 不正行為

TestPublisher

一方、createNonCompliantファクトリメソッドを使用すると、誤動作のあるTestPublisherを作成できます。


null

要素に

__NullPointerExceptionがスローされない

__TestPublisherを見てみましょう。

TestPublisher
  .createNoncompliant(TestPublisher.Violation.ALLOW__NULL)
  .emit("1", "2", null, "3");


ALLOW

NULLに加えて、


TestPublisher.Violation


toも使用できます。


  • REQUEST

    OVERFLOW

    – をスローせずに

    __next()を呼び出すことを許可します。

リクエスト数が不十分な場合、

IllegalStateException

**

__CLEANUP

ON

TERMINATE –

__は終了シグナルの送信を許可します

連続して数回
**

DEFER

CANCELLATION –__はキャンセルシグナルを無視する

発光素子を続ける

5.まとめ

この記事では、** Spring Reactor__プロジェクトからのリアクティブストリームをテストするさまざまな方法について説明しました。

まず、

StepVerifier

を使用して出版社をテストする方法を見ました。それから、

TestPublisherの使い方を見ました。同様に、

TestPublisher__の誤動作による操作方法も見ました。

いつものように、私たちのすべての例の実装はhttps://github.com/eugenp/tutorials/tree/master/spring-5-reactive[Githubプロジェクト]にあります。