1. 概要

この記事では、RxJavaを使用して記述されたコードをテストする方法を見ていきます。

RxJavaで作成している一般的なフローは、 観察可能観察者。 オブザーバブルは、一連の要素であるデータのソースです。 1人以上のオブザーバーがサブスクライブして、発行されたイベントを受信します。

通常、オブザーバブルとオブザーバブルは非同期で別々のスレッドで実行されるため、従来の方法でコードをテストするのは困難です。

幸い、 RxJavaはTestSubscriberクラスを提供し、非同期のイベント駆動型フローをテストする機能を提供します。

2. RxJavaのテスト–従来の方法

例から始めましょう–1からの整数のシーケンスでzipしたい文字のシーケンスがあります。

私たちのテストは、zip形式のobservableによって発行されたイベントをリッスンするサブスクライバーが、整数で圧縮された文字を受信することを表明する必要があります。

従来の方法でこのようなテストを作成するということは、結果のリストを保持し、オブザーバーからそのリストを更新する必要があることを意味します。 整数のリストに要素を追加するということは、オブザーバブルとオブザーバブルが同じスレッドで動作する必要があることを意味します。非同期で動作することはできません。

そのため、RxJavaの最大の利点の1つである、個別のスレッドでのイベントの処理が失われます。

テストの限定バージョンは次のようになります。

List<String> letters = Arrays.asList("A", "B", "C", "D", "E");
List<String> results = new ArrayList<>();
Observable<String> observable = Observable
  .from(letters)
  .zipWith(
     Observable.range(1, Integer.MAX_VALUE), 
     (string, index) -> index + "-" + string);

observable.subscribe(results::add);

assertThat(results, notNullValue());
assertThat(results, hasSize(5));
assertThat(results, hasItems("1-A", "2-B", "3-C", "4-D", "5-E"));

results リストに要素を追加することにより、オブザーバーからの結果を集約しています。 オブザーバーとオブザーバブルは同じスレッドで動作するため、アサーションは適切にブロックされ、 subscribe()メソッドが終了するのを待ちます。

3. TestSubscriberを使用したRxJavaのテスト

RxJavaにはTestSubsriberクラスが付属しており、イベントの非同期処理で機能するテストを作成できます。 これは、オブザーバブルをサブスクライブする通常のオブザーバーです。

テストでは、 TestSubscriber の状態を調べて、その状態についてアサーションを作成できます。

List<String> letters = Arrays.asList("A", "B", "C", "D", "E");
TestSubscriber<String> subscriber = new TestSubscriber<>();

Observable<String> observable = Observable
  .from(letters)
  .zipWith(
    Observable.range(1, Integer.MAX_VALUE), 
    ((string, index) -> index + "-" + string));

observable.subscribe(subscriber);

subscriber.assertCompleted();
subscriber.assertNoErrors();
subscriber.assertValueCount(5);
assertThat(
  subscriber.getOnNextEvents(),
  hasItems("1-A", "2-B", "3-C", "4-D", "5-E"));

TestSubscriberインスタンスをobservableのsubscribe()メソッドに渡します。 次に、このサブスクライバーの状態を調べることができます。

TestSubscriberには、期待を検証するために使用する非常に便利なアサーションメソッドがいくつかあります。 サブスクライバーは、オブザーバーによって5つの放出された要素を受け取る必要があり、 assertValueCount()メソッドを呼び出すことによってそれを表明します。

getOnNextEvents()メソッドを呼び出すことにより、サブスクライバーが受信したすべてのイベントを調べることができます。

assertCompleted()メソッドを呼び出すと、オブザーバーがサブスクライブしているストリームが完了しているかどうかがチェックされます。 assertNoErrors()メソッドは、ストリームのサブスクライブ中にエラーがなかったことをアサートします。

4. 予想される例外のテスト

処理中に、オブザーバブルがイベントを発行しているとき、またはオブザーバーがイベントを処理しているときに、エラーが発生することがあります。 TestSubscriber には、エラー状態を調べるための特別なメソッドがあります。 assertError()メソッドは、例外のタイプを引数として取ります。

List<String> letters = Arrays.asList("A", "B", "C", "D", "E");
TestSubscriber<String> subscriber = new TestSubscriber<>();

Observable<String> observable = Observable
  .from(letters)
  .zipWith(Observable.range(1, Integer.MAX_VALUE), ((string, index) -> index + "-" + string))
  .concatWith(Observable.error(new RuntimeException("error in Observable")));

observable.subscribe(subscriber);

subscriber.assertError(RuntimeException.class);
subscriber.assertNotCompleted();

concatWith()メソッドを使用して、別のオブザーバブルと結合されるオブザーバブルを作成しています。 2番目のオブザーバブルは、次のイベントを発行しているときにRuntimeExceptionをスローします。 assertError()メソッドを呼び出すことにより、TestSubsciberでその例外のタイプを調べることができます。

エラーを受け取ったオブザーバーは処理を停止し、未完了の状態になります。 その状態は、 assertNotCompleted()メソッドで確認できます。

5. 時間ベースのテストObservable

1秒に1つのイベントを発行するObservableがあり、TestSubsciberを使用してその動作をテストするとします。

Observable.interval()メソッドを使用して時間ベースの Observable を定義し、TimeUnitを引数として渡すことができます。

List<String> letters = Arrays.asList("A", "B", "C", "D", "E");
TestScheduler scheduler = new TestScheduler();
TestSubscriber<String> subscriber = new TestSubscriber<>();
Observable<Long> tick = Observable.interval(1, TimeUnit.SECONDS, scheduler);

Observable<String> observable = Observable.from(letters)
  .zipWith(tick, (string, index) -> index + "-" + string);

observable.subscribeOn(scheduler)
  .subscribe(subscriber);

tick observableは、1秒ごとに新しい値を発行します。

テストの開始時は時間ゼロであるため、TestSubscriberは完了しません。

subscriber.assertNoValues();
subscriber.assertNotCompleted();

テストで通過する時間をエミュレートするには、TestSchedulerクラスを使用する必要があります。 TestScheduleradvanceTimeBy()メソッドを呼び出すことにより、その1秒のパスをシミュレートできます。

scheduler.advanceTimeBy(1, TimeUnit.SECONDS);

AdvanceTimeBy()メソッドは、observableが1つのイベントを生成するようにします。 assertValueCount()メソッドを呼び出すことにより、1つのイベントが生成されたと断言できます。

subscriber.assertNoErrors();
subscriber.assertValueCount(1);
subscriber.assertValues("0-A");

文字のリストには5つの要素が含まれているため、オブザーバブルですべてのイベントを発行する場合は、6秒の処理が必要です。 その6秒をエミュレートするには、 AdvanceTimeTo()メソッドを使用します。

scheduler.advanceTimeTo(6, TimeUnit.SECONDS);
 
subscriber.assertCompleted();
subscriber.assertNoErrors();
subscriber.assertValueCount(5);
assertThat(subscriber.getOnNextEvents(), hasItems("0-A", "1-B", "2-C", "3-D", "4-E"));

経過時間をエミュレートした後、TestSubscriberでアサーションを実行できます。 assertValueCount()メソッドを呼び出すことにより、すべてのイベントが生成されたと断言できます。

6. 結論

この記事では、RxJavaでオブザーバブルとオブザーバブルをテストする方法を検討しました。 放出されたイベント、エラー、および時間ベースのオブザーバブルをテストする方法を検討しました。

これらすべての例とコードスニペットの実装は、 GitHubプロジェクトにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。