【Android&Espresso】Navigationで引数付きの画面遷移をした時のUIテストについて

# はじめに

皆さん、ごきげんよう！れぶです！

今回の記事では`Navigation`で引数付きの画面遷移を行った場合、遷移先であるフラグメントのViewをテストする方法について整理しました。

**遷移元から送られた値を遷移先で表示する処理について、UIテスト（`Espresso`）を行いたい**方に特に参考になればと思います。それでは、参りましょう！！

# 開発環境

* MacBook Air
* Android Studio Chipmunk | 2021.2.1
* Kotlin 1.6.10
* compileSdkVersion 33
* targetSdkVersion 33
* minSdkVersion 21
* Espresso 3.4.0

# 前提
## 処理内容
⑴　FirstFragmentのボタン（button1）をタップ
⑵　SecondFragmentに遷移
⑶　数字の10を送る
⑷　SecondFragmentのテキストビュー（textview1）に⑶を表示

※ 今回はあくまで内容を簡単にするために、複雑な処理なしでデータを受け渡します。

![Qiita_Android_BJ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456250/48ec5d0e-cd5d-8020-7b4f-fc675c2d3bd8.png)

## 該当ソースコード
**ナビゲーショングラフ**

```nav_graph.xml
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/firstFragment">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first" >
        <action
            android:id="@+id/action_first_to_second"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" >
        <argument
            android:name="number"
            app:argType="integer" />
    </fragment>
</navigation>
```
**遷移元**
```FirstFragment.kt
private val navController: NavController get() = findNavController()
.....（省略）
view.findViewById<Button>(R.id.button1).setOnClickListener {
    val action = FirstFragmentDirections.actionFirstToSecond(10)
    navController.navigate(action)    
}
```
**遷移先**
```SecondFragment.kt
private val args: ResultFragmentArgs by navArgs()
.....（省略）
val num = args.number
val textview1: TextView = findViewById(R.id.textview1)
textview1.text = "$num"
```
## テスト内容
**SecondFragmentのテキストビュー（textview1）に、FirstFragmentから送られた10が直接表示されるかをテストします。**

# テスト手順
```ExampleInstrumentedTest.kt
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {

    @get:Rule
    val activityScenarioRule: ActivityScenarioRule<MainActivity> = 
        ActivityScenarioRule(MainActivity::class.java)

    @Test
    fun testDisplayArguments() {

        //①TestNavHostControllerを作成
        val navController = 
            TestNavHostController(ApplicationProvider.getApplicationContext())

        //②遷移元のFragmentを起動
        launchFragmentInContainer<FirstFragment>().onFragment { firstFragment ->
            navController.setGraph(R.navigation.nav_graph)
            //navController.setCurrentDestination(R.id.firstFragment)
            Navigation.setViewNavController(
                firstFragment.requireView(),navController)
        }
        onView(withId(R.id.button1)).perform(click())

        //③遷移先のFragmentを起動
        val currentDestinationArg = navController.backStack.last().arguments
        launchFragmentInContainer<SecondFragment>(currentDestinationArg)

        //④遷移先のFragmentのViewをテスト
        onView(withId(R.id.textview1)).check(matches(withText("10")))
    }
}
```

## ①TestNavHostControllerを作成
* Navigationの遷移テスト時に便利
* 今回FirstFragmentに割り当てるために作成

## ②遷移元のFragmentを起動
* FirstFragmentの``FragmentScenario``を作成
* FirstFragmentに``TestNavHostController``のプロパティをセット
* ``setCurrentDestination()``を使うことで現在地を設定できる（今回はFirstFragmentを``startDestination``に設定しているため不要）
* SecondFragmentに遷移するため、ボタンをタップ

## ③遷移先のFragmentを起動
* ``TestNavHostControllerの変数.backStack.last().arguments``で、FirstFragmentから送られた値（Bundle?型）を取得
* 取得した値を引数にSecondFragmentの``FragmentScenario``を作成
* **ナビゲーショングラフで遷移先に引数を設定した場合、launchFragmentInContainerに引数を設定していないと、null argumentsのエラーが出るため注意**

## ④遷移先のFragmentのViewをテスト
* FirstFragmentから送られた値がSecondFragmentのViewに適切に表示されるかをテスト
* 今回テキストビューは「10」と表示されるため、テスト成功

# サンプルコード
因みに自身が作成したブラックジャックができるサンプルアプリ内で、上記の方法を使ったテストコード（displayResultメソッド）を書いているので、もし良かったら参照してみてください。

https://github.com/seiyaleb/KotlinTrialBlackJack/blob/master/app/src/androidTest/java/com/kotlintrialblackjack/ExampleInstrumentedTest.kt

# おわりに
今回は、Navigationで引数が絡む処理のUIテスト方法を超簡単にまとめました。4ステップで実現できるので、意外と簡単だと感じた方も少なくないと思います。

大体はユーザーによる操作をもとにデータの受け渡しをするケースが多いと思うので、上記のやり方が少しでも役立つと嬉しいです。以上です。ありがとうございました！

# 参考サイト
* [Test Navigation（公式ドキュメント）](https://developer.android.com/guide/navigation/navigation-testing)
* [Android Testing - How to validate navController.currentDestination.arguments?（Stack Overflow）](https://stackoverflow.com/questions/63123807/android-testing-how-to-validate-navcontroller-currentdestination-arguments)
* [Supplying nav arguments during espresso tests（Stack Overflow）](https://stackoverflow.com/questions/64072314/supplying-nav-arguments-during-espresso-tests)
* [Android Jetpack コンポーネントのNavigationのプロダクトへの導入手順と実装Tipsの紹介](https://techblog.zozo.com/entry/android-jetpack-navigation)