3
2

More than 3 years have passed since last update.

Navigation componentをテストする

Posted at

Navigation 2.3.0-alpha01からナビゲーションをテストするためのAPIが用意されたので使ってみます。
(2020年2月29日現在、最新バージョンは2.3.0-alpha02です。この記事も2.3.0-alpha02に基づいています。)

従来のNavigationテスト

今までNavigationのテストをする場合、以下のようなテストコードでした。

TitleScreenTest.kt
@RunWith(AndroidJUnit4::class)
class TitleScreenTest {

    @Test
    fun testNavigationToInGameScreen() {
        // Create a mock NavController
        val mockNavController = mock(NavController::class.java)

        // Create a graphical FragmentScenario for the TitleScreen
        val titleScenario = launchFragmentInContainer<TitleScreen>()

        // Set the NavController property on the fragment
        titleScenario.onFragment { fragment ->
            Navigation.setViewNavController(fragment.requireView(), mockNavController)
        }

        // Verify that performing a click prompts the correct Navigation action
        onView(ViewMatchers.withId(R.id.play_btn)).perform(ViewActions.click())
        verify(mockNavController).navigate(R.id.action_title_screen_to_in_game)
    }
}

何をテストしているかというと、R.id.play_btnというViewがクリックされたときに、navigate(R.id.action_title_screen_to_in_gameが呼び出されるかどうか、というのを検証しています。
ただ、呼び出されたからといってActionが望んだ動作をしているのかということまでは保証できません。
例えば、遷移する際にBack stackをpopする場合やargumentを渡す場合などもテストできたら良さそうです。

TestNavHostController

Navigation 2.3.0-alpha01から導入されたのが、TestNavHostControllerです。
https://developer.android.com/reference/kotlin/androidx/navigation/testing/TestNavHostController

前まではNavControllerをモックしていましたが、TestNavHostControllerを使うことでNavController.navigate()後のCurrent destinationやBack stackにアクセスできるようになります。

TitleScreenTest.kt
@RunWith(AndroidJUnit4::class)
class TitleScreenTest {

    @Test
    fun testNavigationToInGameScreen() {
        // Create a TestNavHostController
        val navController = TestNavHostController(
            ApplicationProvider.getApplicationContext())
        navController.setGraph(R.navigation.trivia)

        // Create a graphical FragmentScenario for the TitleScreen
        val titleScenario = launchFragmentInContainer<TitleScreen>()

        // Set the NavController property on the fragment
        titleScenario.onFragment { fragment ->
            Navigation.setViewNavController(fragment.requireView(), navController)
        }

        // Verify that performing a click changes the NavController’s state
        onView(ViewMatchers.withId(R.id.play_btn)).perform(ViewActions.click())
        assertThat(navController.currentDestination?.id).isEqualTo(R.id.in_game)
    }
}

注目するところは、assertThat(navController.currentDestination?.id).isEqualTo(R.id.in_game)という記述。
viewをクリックした後のCurrent destionationのidが、想定しているdestination (Fragment)のidと一致しているか、という検証をしています。
メソッドが呼び出されたかというテストより信頼できます。

Start destinationではないFragmentでのテストを書きたい場合

TestNavHostController.setCurrentDestination()というメソッドが用意されています。
idをセットすることで指定したdestinationからテストを開始することができます。

StartDestination.kt
val navController = TestNavHostController(
                ApplicationProvider.getApplicationContext()
).apply {
    setGraph(R.navigation.navigation)
    setCurrentDesination(R.id.destination)
}

argumentをテストしたい場合

NavBackStackEntry.argumentsで取得できます。
SafeArgsを利用している場合も同様で、argumentのkeyはnavigation.xmlで指定しているargumentの"name"になります。

Args.kt
val argument = navController.currentBackStackEntry.arguments!!["key"]
assertThat(argument).isEqualTo("hoge")

Back stackをテストしたい場合

TestNavHostController.backStackですべてのback stack (List<NavBackStackEntry>)が取得できます。
もしくは、NavController.previeousBackStackEntryで現在の一つ前にあるstackを取り出すことができます。

Stack.kt
val backStack = navController.backstack
val previous = navController.previousBackStackEntry

assertThat(previous!!.destination.id).isEqualTo(R.id.destination)

参考

https://developer.android.com/guide/navigation/navigation-testing
https://github.com/android/architecture-components-samples/tree/master/NavigationBasicSample

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2