liveData{} の UnitTest を書いてみた
coroutines 対応の liveData{} で UnitTest を書いたので、その実装とハマった箇所でも残しておきます。
テストの実装については Google I/O 2019 のセッション動画で紹介されています。
今回は以下のような liveData{} のテストを書いていきます。
fun exampleLiveData(): LiveData<Int> = liveData {
emit(0)
delay(1000)
emit(1)
}
実装
coroutines のテストと AAC のテスト用のライブラリを追加します。
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.2"
testImplementation "androidx.arch.core:core-testing:2.1.0-beta01"
coroutines の Rule を追加します。
セッション動画ではテストのクラス内で testDispatcher を定義していましたが、使い回しができるように Rule にしました。
@Suppress("EXPERIMENTAL_API_USAGE")
class TestCoroutinesRule : TestWatcher() {
private val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
private val testScope: TestCoroutineScope = TestCoroutineScope(testDispatcher)
override fun starting(description: Description?) {
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
Dispatchers.resetMain()
testScope.cleanupTestCoroutines()
}
fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) {
testScope.runBlockingTest(block)
}
}
セッション動画にもある liveData{} を実行するテスト用の Observer を extension で作成します。
fun <T> LiveData<T>.observeForTesting(block: () -> Unit) {
val observer = Observer<T> { Unit }
try {
observeForever(observer)
block()
} finally {
removeObserver(observer)
}
}
実際にテストを書いていきます。
delay を書いている場合でも advanceTimeBy で同じ時間を定義すれば、delay でセットした時間後のテストもきちんと走らせてくれます。
class ExampleRepositoryTest {
@get:Rule
val testCoroutinesRule = TestCoroutinesRule()
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private lateinit var exampleRepository: exampleRepository
@Before
fun setup() {
exampleRepository = ExampleRepositoryImpl()
}
@Test
fun test_exampleLiveData() = testCoroutinesRule.runBlockingTest {
val liveData = exampleRepository.exampleLiveData()
liveData.observeForTesting {
assertEquals(liveData.value, 0)
advanceTimeBy(1000)
assertEquals(liveData.value, 1)
}
}
}
ハマった箇所
・liveData{} の Dispatchers を指定していた場合 liveData{} の処理が行われなかった
今回のテストでは Dispatchers#setMain で Dispatchers.Main をテストのものへ置き換えることができますが、liveData{} に Dispatchers.IO や Default をセットしていた場合に liveData{} の処理が実行されませんでした。
liveData{} に対して外から CoroutineContext を渡すようにした方がいいのかな🤔
・AndroidX × Robolectric のテスト環境では liveData{} の処理が行われなかった
AndroidX の JUnit で Robolectric を使っていると Thread 周りでエラーになり liveData{} の処理が実行されませんでした。
(ハマった箇所について解決策などご存知の方がいたら教えていただきたい…😥)