RxJavaをテストするには?
1. 概要
この記事では、RxJavaを使用して記述されたコードをテストする方法を見ていきます。
RxJavaで作成している一般的なフローは、ObservableとObserver.で構成されています。オブザーバブルは、要素のシーケンスであるデータのソースです。 1人以上のオブザーバーがサブスクライブして、発行されたイベントを受信します。
通常、オブザーバーとオブザーバブルは非同期の方法で別々のスレッドで実行されます。これにより、従来の方法でコードをテストするのが難しくなります。
幸い、RxJava provides a TestSubscriber class which gives us the ability to test asynchronous, event-driven flow.
2. RxJavaのテスト–従来の方法
Let’s start with an example –1からの整数のシーケンスでzipする文字のシーケンスがあります。
テストでは、zipされたobservableによって発行されたイベントをリッスンするサブスクライバーが、整数で圧縮された文字を受け取ることをアサートする必要があります。
このようなテストを従来の方法で記述することは、結果のリストを保持し、オブザーバーからそのリストを更新する必要があることを意味します。 整数のリストに要素を追加することは、オブザーバブルとオブザーバーが同じスレッドで動作する必要があることを意味します。非同期で動作することはできません。
そのため、RxJavaの最大の利点の1つである、別々のスレッドでのイベントの処理が失われます。
限定バージョンのテストは次のようになります。
List letters = Arrays.asList("A", "B", "C", "D", "E");
List results = new ArrayList<>();
Observable 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 letters = Arrays.asList("A", "B", "C", "D", "E");
TestSubscriber subscriber = new TestSubscriber<>();
Observable 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"));
オブザーバブルのsubscribe()メソッドにTestSubscriberインスタンスを渡します。 次に、このサブスクライバーの状態を調べることができます。
期待を検証するために使用するTestSubscriber has some very useful assertion methods。 サブスクライバーは、オブザーバーによって5つの放出された要素を受け取る必要があり、assertValueCount()メソッドを呼び出すことによってそれを表明します。
getOnNextEvents()メソッドを呼び出すことにより、サブスクライバーが受信したすべてのイベントを調べることができます。
assertCompleted()メソッドを呼び出すと、オブザーバーがサブスクライブしているストリームが完了しているかどうかがチェックされます。 assertNoErrors()メソッドは、ストリームのサブスクライブ中にエラーがなかったことを表明します。
4. 予想される例外のテスト
処理中に、オブザーバブルがイベントを発行しているとき、またはオブザーバーがイベントを処理しているときに、エラーが発生することがあります。 TestSubscriberには、エラー状態を調べるための特別なメソッドがあります。これは、例外のタイプを引数として取るassertError()メソッドです。
List letters = Arrays.asList("A", "B", "C", "D", "E");
TestSubscriber subscriber = new TestSubscriber<>();
Observable 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 letters = Arrays.asList("A", "B", "C", "D", "E");
TestScheduler scheduler = new TestScheduler();
TestSubscriber subscriber = new TestSubscriber<>();
Observable tick = Observable.interval(1, TimeUnit.SECONDS, scheduler);
Observable observable = Observable.from(letters)
.zipWith(tick, (string, index) -> index + "-" + string);
observable.subscribeOn(scheduler)
.subscribe(subscriber);
tick observableは、1秒ごとに新しい値を発行します。
テストの開始時は時間ゼロであるため、TestSubscriberは完了しません。
subscriber.assertNoValues();
subscriber.assertNotCompleted();
テストで経過する時間をエミュレートするには、TestSchedulerクラスを使用する必要があります。 TestSchedulerでadvanceTimeBy()メソッドを呼び出すことにより、その1秒のパスをシミュレートできます。
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
advanceTimeBy()メソッドは、observableが1つのイベントを生成するようにします。 assertValueCount()メソッドを呼び出すことにより、1つのイベントが生成されたと断言できます。
subscriber.assertNoErrors();
subscriber.assertValueCount(1);
subscriber.assertValues("0-A");
lettersのリストには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 projectにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。