LiveDataのUnitTest

ちょっとやっかいな部分もあったので、LiveDataのテストをどう書くのかまとめました。

build.gradle

必要なものを追加します。LiveDataを使うので、もちろんArchitectureComponentを追加してます。

mockito-kotlin はmockitoをkotlinでいい感じに使うやつです。mockitoをそのまま使うとNull Safetyが影響したりするので、入れておくと良いと思います。

core-testing はLiveDataをテストする上で必要なものがあります。

同期処理

テスト対象

単純なViewModelをテストしてみます。この例だと、 fetchNameが呼ばれたらLiveDataに文字列を流してるだけです。

テスト

テストコードはこんな感じです。

16行目: ここでは、Observerのmockを作成しています。mockitoを使って最後にverifyで値をチェックするためです。

17行目: 通常、LiveDataをobserveするときは LifecycleOwner が必要になりますが、テストの場合はこれを用意するのが大変なのと必要性がないので、 observeForever を使います。

21行目: mockitoのverify機能を使ってObserverの onChanged が期待される値で呼び出されたかを確認しています。

5行目: 最後に、 InstantTaskExecutorRule ですが、詳細な解説はだいぶ長くなるので、おまけとして最後に書いてます。LiveDataのテストを書くときは必須になります。


非同期処理

テスト対象

非同期で処理をして、LiveDataのpostValueで値を流してるだけです。

問題

これを同期処理と同じようにやろうとすると、テスト対象の処理が終わる前にverifyにて検証が行われてしまい、まだ呼び出されていないので、テストが失敗します。

テスト用Observer作成

この問題を回避するために、テスト用のObserverを作ります。

CountDownLatch を使って、 onChanged が呼ばれた回数を数えることで処理を制御しています。

コンストラクタで onChanged が呼ばれるであろう期待する回数を指定できます。 await でその回数が呼ばれるまで待機します。一応、処理が終わらないということを回避するためにタイムアウトを設定しています。

onChanged に渡された値はListに追加していって後で検証できるようにしてあります。

テスト

テストはmockで作ってたObserverを使わずに、先程作ったTestObserverを使うようにしています。

16–17行目: テスト用のObserverを生成して、それを使ってます。

21行目: 非同期処理が終わるまで(onChangedが指定回数呼び出されるまで)待機します。

23行目: onChangedに渡された値を取得して、検証しています。

こんな感じで非同期処理をテストできるようになります。実はこのテスト用のObserverを同期処理で使うとMockitoもいらなくなったりしますが。


おまけ: InstantTaskExecutorRuleについて

LiveDataのsetValueやpostValue実行されるメソッドが重要になってきます。setValueは必ずMainスレッドから呼び出さなければなりません。また、postValueは最終的にMainスレッドでObserverのonChangedを呼び出します。

このあたりがテスト実行時にやっかいな感じになります。

このスレッドの管理には、 AsyncTaskExecutorDefaultTaskExecutor が通常使用されています。

LiveDataのsetValue実行時には assertMainThread でメインスレッドで次のように呼び出されたかをチェックしています。

ここで、 AsyncTaskExecutor が使われて、最終的に DefaultTaskExecutor に処理が委譲されてメインスレッドかをチェックしています。

これをそのまま、テストで実行してしまうと、以下のようなエラーになってしまいます。

java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.

(もし、build.gradleのtestOptionに unitTests.returnDefaultValues = true を設定している場合は NullPointerException になる)

これではテストが実行できないので、この部分を差し替えてくれるのが、 InstantTaskExecutorRule です。

JUnitのRuleの機能を使って、テスト開始時に AsyncTaskExecutor で使われてる、 TaskExecutorDefaultTaskExecutor を使うのではなく、 テスト用に差し替えています。

コードは ココ にあるので、確認してみてください。例えば、 isMainThread のメソッドは常にtrueを返すようにしてます。

なので、LiveDataのテストには、Ruleに InstantTaskExecutorRule を指定する必要があります。

これは、 android.arch.core:core-testing にあるので、build.gradleに別に追加します。

解説としてはこんな感じです。