ちょっとやっかいな部分もあったので、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を呼び出します。
このあたりがテスト実行時にやっかいな感じになります。
このスレッドの管理には、 AsyncTaskExecutor
と DefaultTaskExecutor
が通常使用されています。
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
で使われてる、 TaskExecutor
を DefaultTaskExecutor
を使うのではなく、 テスト用に差し替えています。
コードは ココ にあるので、確認してみてください。例えば、 isMainThread
のメソッドは常にtrueを返すようにしてます。
なので、LiveDataのテストには、Ruleに InstantTaskExecutorRule
を指定する必要があります。
これは、 android.arch.core:core-testing
にあるので、build.gradleに別に追加します。
解説としてはこんな感じです。