UnitテストでViewModelのonClearedをテストする

ふとAACのViewModelのonClearedメソッドをテストしたくなったので、 3つのやりかたを紹介します。

環境は

"junit:junit:4.12"
"androidx.test:rules:1.1.1"
"androidx.test:runner:1.1.1"
"androidx.test.ext:junit:1.1.0"
"com.nhaarman:mockito-kotlin-kt1.1:1.5.0"
"org.robolectric:robolectric:4.1"

になります。

また、サンプルコードは satoshun-android-example/Testsにあるので、参考してください😊

1. ViewModelStoreを使う

ViewModelProviders.of(activity).get(class)からViewModelを取得したときに、取得したViewModelはViewModelStoreにキャッシュされます。このViewModelStoreはFragmentActivityから取得できるので、次のように書くことでViewModelのonClearedをテストすることが出来ます。

@RunWith(AndroidJUnit4::class)
class BaseViewModelTest {
  @get:Rule val activityRule = ActivityTestRule(FragmentActivity::class.java)

  @Test
  fun `dispose a coroutine when finished lifecycle of ViewModel`() {
    activityRule.activity.viewModelStore.clear() // ViewModelが開放される
  }
}

このテストはコード的には簡単ですが、ViewModelStoreがViewModelを管理していることを知っている、内部実装の詳細まで知っているため、テストとしてふさわしくない可能性があります。

なので、素直にonDestroyをコールするテストも書いてみます。

2. Instrumentation.callActivityOnDestroyを使う

Instrumentationクラスを使うことでActivityのライフサイクルをコントロールすることが出来ます。 InstrumentationInstrumentationRegistryクラスから取得することができ、次のように書くことで、onDestroyをコールすることができます。

@Test
fun `dispose a coroutine when finished lifecycle of ViewModel 2`() {
  // onDestroyがコールされViewModelが開放される
  InstrumentationRegistry.getInstrumentation().callActivityOnDestroy(activityRule.activity)
}

ActivityのonDestroyがコールされ、ViewModelのonClearedもコールされます!

以下追記

3. ActivityScenarioを使う

新しく追加されたActivityScenarioを使うとよりすっきりとonDestroyを表現することが出来ます。

@Test
fun `dispose a coroutine when finished lifecycle of ViewModel 3`() {
  val scenario = ActivityScenario.launch(FragmentActivity::class.java)
  ...
  // onDestroy状態になり、ViewModelのonClearedがコールされる
  scenario.moveToState(Lifecycle.State.DESTROYED)
}

ActivityScenarioはmoveToStateを介して、Activityのライフサイクルを操作することが出来ます。 今回はActivityをonDestroy状態にしたいので、scenario.moveToState(Lifecycle.State.DESTROYED)をコールします。

これで、UnitテストでViewModel.onClearedのテストをすることが出来ます!!

まとめ

以上になります。Happy Testing😊😊😊

Written by
あんどろいどでぃべろっぱぁー🍎