In my Espresso test I have set up to create an Activity with a Fragment and Navigation Graph:
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()
}
}
And my Activity and Fragment load up fine, but if I log the result of findNavController().currentDestination
it shows me the first screen in my flow (I am trying to test from the 4th screen)
This is not such a big problem except that when in my test setup the Fragment tries to open a new fragment I get the following error:
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
So the Navigation
thinks I'm on the startDestination
fragment and not the fragment that I'm trying to load, so I can't navigate away FROM my Fragment in test. I am not trying to pop, I know there would be no backstack.
The following code is what I use to navigate inside my fragment, which works fine "in flow".
findNavController()
.navigate(
R.id.action_screen4_to_ErrorScreen,
null
)
I have tried
requireView()
insteadbinding.root.findNavController()
requireActivity().findNavController(requireView().id)
Navigation.findNavController(requireActivity(), R.id.fragmentContainerId)
Navigation.findNavController(requireView())
I have created a repo that demonstrates the problem: https://github.com/qbalsdon/espressoTestFramework
So I found out that this is quite possible, and you can load a fragment into any activty if you use deep linking rather than trying to set the destination after the activity is loaded:
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
}
}
}
And then the usage:
@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
)
}