architecture-samples icon indicating copy to clipboard operation
architecture-samples copied to clipboard

launchFragmentInHiltContainer doesn't work with navGraphViewModels

Open Shoorie opened this issue 2 years ago • 2 comments

Hi, For test purposes I've modified the code of launchFragmentInHiltContainer to this:

const val THEME_EXTRAS =
  "androidx.fragment.app.testing.FragmentScenario.EmptyFragmentActivity.THEME_EXTRAS_BUNDLE_KEY"

inline fun <reified T : Fragment> launchFragmentInHiltContainer(
  fragmentArgs: Bundle? = null,
  @StyleRes themeResId: Int = R.style.FragmentScenarioEmptyFragmentActivityTheme,
  navHostController: TestNavHostController? = null,
  crossinline action: Fragment.() -> Unit = {}
) {

    val componentName = ComponentName(
      ApplicationProvider.getApplicationContext(),
      HiltTestActivity::class.java
    )

    val intent = Intent
      .makeMainActivity(componentName)
      .putExtra(THEME_EXTRAS, themeResId)

    launch<HiltTestActivity>(intent).onActivity { activity ->

        val fragment = activity
          .supportFragmentManager
          .fragmentFactory
          .instantiate(Preconditions.checkNotNull(T::class.java.classLoader), T::class.java.name)
          .also { it.arguments = fragmentArgs }

        fragment.viewLifecycleOwnerLiveData.observeForever {
            if (it != null) {
                navHostController?.let { controller ->
                    controller.setGraph(R.navigation.nav_graph)
                    controller.setCurrentDestination(R.id.tasks_fragment_dest)
                    Navigation.setViewNavController(fragment.requireView(), controller)
                }
            }
        }

        activity.supportFragmentManager
          .beginTransaction()
          .add(android.R.id.content, fragment, "")
          .commitNow()

        fragment.action()
    }
}

Then I've updated test designated for redirecting user to add_edit_task_fragment_dest:


@Test
  fun clickAddTaskButton_navigateToAddEditFragment() {
    // GIVEN - On the home screen
    val navController = TestNavHostController(ApplicationProvider.getApplicationContext())
    navController.setViewModelStore(ViewModelStore())
    launchFragmentInHiltContainer<TasksFragment>(Bundle(), R.style.AppTheme, navController)

    // WHEN - Click on the "+" button
    onView(withId(R.id.add_task_fab)).perform(click())

    // THEN - Verify that we navigate to the add screen
    assertEquals(navController.currentDestination?.id, id.add_edit_task_fragment_dest)
  }

In TaskFragment changed only the line with propagating view model lazily but using navGraphViewModels instead of viewModels like this:

private val viewModel
    by navGraphViewModels<TasksViewModel>(R.id.nav_graph) { defaultViewModelProviderFactory }

The all tests consisting of navigation just fails:

IllegalStateException: Fragment TasksFragment{fe2ac75} (8c347050-d52e-4da4-ad2d-e4694c0ceb0f id=0x1020002 tag=) does not have a NavController set

Any workarounds?

Shoorie avatar Mar 24 '22 07:03 Shoorie

What if you use this instead?

val navController = TestNavHostController(ApplicationProvider.getApplicationContext())
navController.setGraph(R.navigation.nav_graph)

launchFragmentInHiltContainer<TasksFragment>(navHostController = navController)

cjacky475 avatar Sep 30 '22 14:09 cjacky475

I am facing same issue. Any work around?

val mockNavController = mock(NavController::class.java)
       launchFragmentInHiltContainer2<SearchListFragment>
       {
           Navigation.setViewNavController(this.requireView() , mockNavController)
       }

in my fragment I am using following. private val navController: NavController by lazy { findNavController() }

Error Fragment SearchListFragment{f12c5e3} (7cd3d967-c7bb-441c-9572-f6acd3c59ae2 id=0x1020002 tag=) does not have a NavController set

Iqra786 avatar Nov 03 '23 21:11 Iqra786