fly1tkg blog

RxJavaのテストコードを書く

はじめに

最近のAndroidアプリ開発ではClean Architectureなど、設計の議論がすすみ、テストコードのためのツール群も増えたことで、テストコードを書くコストが減ってきているように感じます。

また、もう一つの流行としてRxJavaの採用事例が増えてきたように感じます。

JavaのJUnitテストやAndroidのテストにおいて、RxJavaのテストコードをどのように書くべきか、調べたことを書いていきたいと思います。

TestSubscriberについて

Subscriptionは非同期処理なので、ユニットテストするには同期処理が必要になります。(非同期処理のままにしておくと、Observableから値が返ってくる前にテストが終了してしまいます。)

公式のテスト方法はTestSubscriberクラスを利用することです。これはRxJavaライブラリに含まれています。

正常系のテスト

以下がコードのサンプルです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Observable<String> observable = Observable.just("foo");

// Testsubscriberを作成する
TestSubscriber<String> testSubscriber = new TestSubscriber<>();

// テストしたObservableに先程のSubscriberをsubscribeさせます
observable.subscribe(testSubscriber);

// 処理が完了するのを待ちます
testSubscriber.awaitTerminalEvent();

// 検証します
testSubscriber.assertNoErrors();
testSubscriber.assertValue("foo");

TestSubscriber#awaitTerminalEventはObservableSubscriberのonCompletedまたはonErrorを呼ぶまでブロックしてくれます。longとTimeUnitを引数に指定することで、タイムアウトも指定することができます

異常系のテスト

例外の発生をテストする場合は以下のようにかけます。

1
2
3
4
5
6
7
8
Observable<String> observable = Observable.error(new IllegalStateException());

TestSubscriber<String> testSubscriber = new TestSubscriber<>();
observable.subscribe(testSubscriber);

testSubscriber.awaitTerminalEvent();

testSubscriber.assertError(IllegalStateException.class);

TestSubscriber#assertErrorでは引数に例外クラスまたはインスタンスを指定することで発生を検証できます。

ストリームとSubscriberの状態のテスト

Testsubscriber#assertValueはストリームの一番最後の値しか検証できません。1つの値しか返さない場合はこれでも良いのですが、複数の値が流れてくる場合は工夫する必要があります。

以下のコードでは説明を簡単にするためにTestSubscriberを直接操作しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
TestSubscriber<Integer> testSubscriber = new TestSubscriber<>();

// onNextに値が渡っていないことを検証できます
testSubscriber.assertNoValues();

testSubscriber.onNext(1);
// onNextに渡ってきた値を検証する
assert(1 == testSubscriber.getOnNextEvents().get(0));

testSubscriber.onNext(2);
testSubscriber.onNext(3);
// getOnNextEventsはonNextで受け取った値を全て保持しています
assert(1 == testSubscriber.getOnNextEvents().get(0));
assert(2 == testSubscriber.getOnNextEvents().get(1));
assert(3 == testSubscriber.getOnNextEvents().get(2));

// onNextで受け取った値の配列を検証できます
testSubscriber.assertReceivedOnNext(Arrays.asList(1, 2, 3));

// 受け取った値の数を検証できます
testSubscriber.assertValueCount(3);

// subscriberの状態も検証できる
testSubscriber.assertNotCompleted();
testSubscriber.assertNoTerminalEvent();

testSubscriber.onCompleted();
testSubscriber.assertTerminalEvent();

testSubscriber.unsubscribe();
testSubscriber.assertUnsubscribed();

TestSubscriber#getOnNextEventsはonNextで受け取った値を全て保持しています。これを利用すると特定の場所に特定の値が流れてくることを検証できます。

またTestsubscriber#assertReceivedOnNextでonNextで受け取った値をリストで比較して検証できます。

Testsubscriber#assertValueCountはonNextで受け取った値の数を検証できます。

TestSubscriber#assertNotCompletedはonCompletedが呼ばれていないこと、

Testsubscriber#assertNoTerminalEventはそれに加えてonErrorも呼ばれていないことを検証できます。

Testsubscriber#assertUnsubscribedはunsubscribeされていることを検証できます。

おわりに

RxJavaにはテスト用のTestSubscriberクラスが準備されています。

RxJavaではObservable#flatMapによって個々のObservableを簡単に連結できるので、小さい単位でObservableを作成するコードを作成し、それぞれの入出力のテストコードを書いておくことで、堅牢で柔軟なモジュールが作成できるのではと思いました。

Gist

https://gist.github.com/fly1tkg/3c29dc34682b64076058

参考

http://reactivex.io/RxJava/javadoc/rx/observers/TestSubscriber.html