CoroutineLiveDataをどうにかしてテストする
関数androidx.lifecycle.livedata
からCoroutineLiveData
というLiveData
を取得できる(CoroutineLiveData
はinternal
クラスなので実際見えるのはLiveData
)。例えば次のような感じで中断関数を渡してやるとライフサイクルとかとの関係をいい感じにやってくれつつemitSource()
で渡したLiveData
をセットしてくれる(普通の値の場合はemit()
を使う)。
val itemsSource: LiveData<List<Item>> = livedata { val source: LiveData<List<Item>> = dao.getItemsSource() emitSource(source) val items: List<Item> = restApi.fetchItems() dao.insert(items) }
今回はこのLiveDataの値(=中断関数のロジック)に関するテストを書くことを考える。
書いたコードなど
関係ありそうな依存関係:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.61" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3" implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.2.0" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.3' testImplementation "androidx.arch.core:core-testing:2.1.0" testImplementation 'org.robolectric:robolectric:4.3.1'
- テスト対象: nasnen/RecordScheduleRepository.kt at master · akihito104/nasnen · GitHub
- テスト: nasnen/RecordScheduleRepositoryTest.kt at master · akihito104/nasnen · GitHub
- テストルール: nasnen/CoroutineTestRule.kt at master · akihito104/nasnen · GitHub
なにをやっているか
CoroutineLiveData
は内部でLooper
を使うらしいのでJVMテストではこれをモックしなければならない。そのためにRobolectricが必要。LiveData
をテストでobserveするためにはInstantTaskExecutorRule
が必要なのでarch.core-testingパッケージを入れている。- テストのスレッドとテスト対象のスレッドとを同じにしたいのでテストの
Dispatcher
を注入する。TestCoroutineDispatcher
というやつがあるのでこれを使う。テスト対象ではこのDispatcher
をlivedata()
関数に渡す。 CoroutineLiveData
をobserveしたあとでRobolectricのshadowOf(getMainLooper()).idle()
を呼べば全部の処理が流れるので、テストコードでアサーションできるようになる。めでたし。- と思ったけど、テスト対象側(
livedata()
の中)で起動したコルーチンで起きた例外をテスト側では検知できない。なのでDispatcher
と一緒にCoroutineExceptionHandler
も注入してやる必要がある。今回はライブラリに入っていたTestCoroutineExceptionHandler
を使っている。