StepVerifierとTestPublisherを使って反応型ストリームをテストする

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プロジェクト]にあります。