Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Architecture Componentとテスト2019年7月版 / Te...

tkmnzm
July 25, 2019

Android Architecture Componentとテスト2019年7月版 / Testing Android Architecture Components in 2019-07

tkmnzm

July 25, 2019
Tweet

More Decks by tkmnzm

Other Decks in Programming

Transcript

  1. ObserveForever // Observer にどんな値が渡ってくるかを記録するためのspy val spyObserver = spyk<Observer<String>>() spy をobserveForever

    viewmodel.strLiveData.observeForever(spyObserver) // strLiveData をTest にする実装 viewmodel.changeText("Test") verify { // onChanged の引数をVerify で検証 spyObserver.onChanged("test") }
  2. 典型的なViewModelのテスト class TaskViewModelTest { @get:Rule var instantExecutorRule = InstantTaskExecutorRule() @Test

    fun test() { val taskRepositoty = mock/fake TaskRepository val viewModel = TaskViewModel(taskRepositoty) val spyObserver = spyk<Observer<String>>() viewmodel.notifyText.observeForever(spyObserver) viewmodel.addTask("Test") verify { spyObserver.onChanged("add task:Test !") } } }
  3. Testing with ViewModelScope ViewModelScope内でdelayがあるときは、delayした分の時間をすすめ て処理を再開してあげる val testDispatcher = TestCoroutineDispatcher() ..

    @Test fun testDelay() { val viewModel = TaskViewModel() // ViewModelScope 内でdelay(1_000) viewModel.delayMathod() // TestCoroutineDispatcher#advanceTimeBy // advanceUntilIdle() というのもある testDispatcher.advanceTimeBy(1_000) // assertion }
  4. SavedStateHandler class MyViewModel(val savedState: SavedStateHandle) : ViewModel() { val text

    = savedStateHandle.getLiveData<String>("text") fun updateText() { savedStateHandle.set("text", "test") } }
  5. SavedStateHandler class SavedStateViewModelTest { @get:Rule val testRule = InstantTaskExecutorRule() @Test

    fun test() { // 空のSavedStateHandle を渡す val target = SavedStateViewModel(SavedStateHandle()) target.updateText() val spyObserver = spyk<Observer<String>>() target.text.observeForever(spyObserver) verify(spyObserver).onChanged("test") } }
  6. Dao Interface class TasksRepository (val tasksDao: TasksDao) val db =

    Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "Tasks.db").build() val repo = TasksRepository(db.tasksDao()) モックへの置き換え val mock = mockk<Taskdao>() val repo = TasksRepository(mock)
  7. in‑memory database @RunWith(AndroidJUnit4::class) class TaskDaoTest { private lateinit var db:

    TestDatabase @Before fun setUp() { val context = ApplicationProvider .getApplicationContext<Context>() db = Room.inMemoryDatabaseBuilder( context, TestDatabase::class.java).build() } @Test fun test() { // Room のin‑memory database のDAO 実装を注入 val repo = TasksRepository(db.tasksDao()) } @After fun tearDown() { db.close() }
  8. TestListenableWorkerBuilder val context = ApplicationProvider.getApplicationContext() val worker = TestListenableWorkerBuilder<MyWorker>( inputData

    = workDataOf("key" to "value") context = context).build() val result = worker.startWork().get() assertThat(result, equalTo(Result.success()))
  9. TestWorkerBuilder Executorが差し替えられるのがTestListenableWorkerBuilderとの違い val worker = TestWorkerBuilder<MyWorker>( context = context, executor

    = executor, inputData = workDataOf("key" to "value")).build() val result = worker.startWork().get() assertThat(result, equalTo(Result.success()))
  10. WorkerFactory class MyWorkerFactory : WorkerFactory() { override fun createWorker( appContext:

    context, workerClassName: name, workerParameters: params) : ListenableWorker? { if (name == MyWorker::class.java.name) { // MyWorker の依存クラスを追加 return MyWorker(context, params, MyDependency()) } return null } }
  11. WorkerFactory Custom Appliction class MyApp : Application() { override fun

    onCreate() { super.onCreate() val config = Configuration.Builder() .setWorkerFactory(MyWorkerFactory()).build() WorkManager.initialize(this, config) } } AndroidManifest.xmlでデフォルトのInitializerをremoveする <provider android:name="androidx.work.impl.WorkManagerInitializer" android:authorities="${applicationId}.workmanager‑init" tools:node="remove" />
  12. WorkerFactory @Before fun setUp() { val context = ApplicationProvider.getApplicationContext() val

    config = Configuration.Builder() .setWorkerFactory(TestMyWorkerFactory()) .build() WorkManagerTestInitHelper. initializeTestWorkManager(context, config) }
  13. Robolectric Support WorkManagerのテストはLocal Testでも動作する Issueでサポートしない旨の回答があったが、無事に対応された模様 WorkManagerの初期化をしないと実行できなかった @Before fun setUp() {

    val context = ApplicationProvider.getApplicationContext() // WorkManagerTestInitHelper をつかう // WorkManager.initialize(context, config) は // Instrumentation Test で実行するとエラーになる WorkManagerTestInitHelper. initializeTestWorkManager(context, config) }
  14. with Fragment val navController = mockk<NavController>(relaxed = true) val scenario

    = launchFragmentInContainer<TaskFragment>() scenario.onFragment { Navigation.setViewNavController(it.view!!, navController) } ... verify { navController.navigate(TaskFragmentDirections .actionTaskFragmentToTaskListFragment()) }