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{} の処理が実行されませんでした。


(ハマった箇所について解決策などご存知の方がいたら教えていただきたい…😥)



この記事が気に入ったら、サポートをしてみませんか?気軽にクリエイターを支援できます。

3

NUmero

関西の IT 企業で Android アプリ作ってる人です。 趣味でも Android のアプリ作っています。 Material Design 大好き。
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。