浓缩咖啡测试:TestNavHostController.setCurrentDestination 设置不正确

Espresso test: TestNavHostController.setCurrentDestination not being set correctly

在我的 Espresso 测试中,我已设置创建一个带有片段和导航图的 Activity:

protected inline fun <reified A : AppCompatActivity, reified F : Fragment> launchFragment(
    @NavigationRes navigationResource: Int,
    @IdRes fragmentId: Int
) {
    launchFragmentInHiltContainer<A, F> {
        navController.setGraph(navigationResource)
        navController.setCurrentDestination(fragmentId) //<-- I believe the problem is here
        this.viewLifecycleOwnerLiveData.observeForever {
            //navController.setCurrentDestination(fragmentId)
            Navigation.setViewNavController(this.requireView(), navController)
        }
    }
}

inline fun <reified A : AppCompatActivity, reified F : Fragment> launchFragmentInHiltContainer(
    fragmentArgs: Bundle? = null,
    fragmentFactory: FragmentFactory? = null,
    crossinline action: Fragment.() -> Unit = {}
) {
    val startActivityIntent = Intent.makeMainActivity(
        ComponentName(
            ApplicationProvider.getApplicationContext(),
            A::class.java
        )
    )

    ActivityScenario.launch<A>(startActivityIntent).onActivity { activity ->
        fragmentFactory?.let {
            activity.supportFragmentManager.fragmentFactory = it
        }
        val fragment: Fragment = activity.supportFragmentManager.fragmentFactory.instantiate(
            Preconditions.checkNotNull(F::class.java.classLoader),
            F::class.java.name
        )

        fragment.arguments = fragmentArgs
        activity.supportFragmentManager
            .beginTransaction()
            .add(android.R.id.content, fragment, "") //<-- I have also tried the Id of the fragment container in the activity
            .commitNow()

        fragment.action()
    }
}

我的 Activity 和 Fragment 加载正常,但如果我记录 findNavController().currentDestination 的结果,它会显示流程中的第一个屏幕(我正在尝试从第 4 个屏幕开始测试)

这不是什么大问题,只是在我的测试设置中片段试图打开一个新片段时出现以下错误:

Caused by: java.lang.IllegalArgumentException: Navigation action/destination packageName:id/action_screen4_to_ErrorScreen cannot be found from the current destination Destination(packageName:id/screen1Fragment) label=screen1 class=screen1Fragment

所以 Navigation 认为我在 startDestination 片段上而不是我要加载的片段,所以我无法在测试中离开我的片段。我不是想弹出,我知道不会有 backstack。

以下代码是我用来在我的片段中导航的代码,它在“流中”运行良好。

findNavController()
    .navigate(
        R.id.action_screen4_to_ErrorScreen,
        null
    )

我试过了

我创建了一个演示问题的存储库:https://github.com/qbalsdon/espressoTestFramework

所以我发现这是很有可能的,如果您使用深度链接而不是在加载 activity 之后尝试设置目标,则可以将片段加载到任何活动中:

protected inline fun <reified A : AppCompatActivity> launchFragment(
    @NavigationRes navigationResource: Int,
    @IdRes fragmentId: Int,
    fragmentArgs: Bundle? = null
) {
    launchFragmentWithDeepLink<A>(fragmentArgs, null, navigationResource, fragmentId)
}

inline fun <reified A : AppCompatActivity> launchFragmentWithDeepLink(
    fragmentArgs: Bundle? = null,
    fragmentFactory: FragmentFactory? = null,
    @NavigationRes navigationResource: Int,
    @IdRes fragmentId: Int
) {

    val launchIntent = NavDeepLinkBuilder(InstrumentationRegistry.getInstrumentation().targetContext)
        .setGraph(navigationResource)
        .setComponentName(A::class.java)
        .setDestination(fragmentId)
        .setArguments(fragmentArgs)
        .createTaskStackBuilder().intents[0]

    ActivityScenario.launch<A>(launchIntent).onActivity { activity ->
        fragmentFactory?.let {
            activity.supportFragmentManager.fragmentFactory = it
        }
    }
}

然后是用法:

@get:Rule(order = 1)
override val hiltRule = HiltAndroidRule(this)
@get:Rule(order = 2)
val activityTestRule = ActivityScenarioRule(MainActivity::class.java)

private fun showFragment() {
    launchFragment<MainActivity>(
        R.navigation.navigation_graph,
        R.id.my_fragment_id
    )
}