Mervin Hemaraju
Mervin Hemaraju

Reputation: 2175

Android Espresso does not have a NavController set Error

I am trying to test a fragment in my navigation architecture and my test is as follows:

test.kt

@RunWith(AndroidJUnit4::class)
@MediumTest
internal class AddingAccountTest{

@get:Rule
var activityRule: ActivityTestRule<MainActivity>
        = ActivityTestRule(MainActivity::class.java)

@Before fun loadCorrespondingFragment(){

}

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

    // Create a graphical FragmentScenario for the TitleScreen
    val titleScenario = launchFragmentInContainer<AddAccountFragment>(Bundle(), themeResId = R.style.Locky_Theme)

    // Set the NavController property on the fragment
    titleScenario.onFragment { fragment ->
        Navigation.setViewNavController(fragment.requireView(), navController)
    }
    onView(withId(R.id.Account_Name)).check(matches(isEnabled()))
}
}

But when i run it, i am getting the follow errors:

java.lang.IllegalStateException: View androidx.constraintlayout.widget.ConstraintLayout{65be158 V.E...... ......I. 0,0-0,0 #7f0a0129 app:id/cl_layout} does not have a NavController set
at androidx.navigation.Navigation.findNavController(Navigation.java:84)
at androidx.navigation.fragment.NavHostFragment.findNavController(NavHostFragment.java:118)
at androidx.navigation.fragment.FragmentKt.findNavController(Fragment.kt:29)
at com.th3pl4gu3.locky_offline.ui.main.add.account.AddAccountFragment.observeBackStackEntryForLogoResult(AddAccountFragment.kt:89)
at com.th3pl4gu3.locky_offline.ui.main.add.account.AddAccountFragment.onViewCreated(AddAccountFragment.kt:72)

The error is occurring in my AddAccountFragment.kt on line 89 which contains this code:

AccountFragment.kt

val navBackStackEntry = findNavController().getBackStackEntry(R.id.Fragment_Add_Account)

This code is used to get backstack entry data as follows:

private fun observeBackStackEntryForLogoResult() {
    // After a configuration change or process death, the currentBackStackEntry
    // points to the dialog destination, so you must use getBackStackEntry()
    // with the specific ID of your destination to ensure we always
    // get the right NavBackStackEntry
    val navBackStackEntry = findNavController().getBackStackEntry(R.id.Fragment_Add_Account)

    // Create our observer and add it to the NavBackStackEntry's lifecycle
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME
            && navBackStackEntry.savedStateHandle.contains(KEY_ACCOUNT_LOGO)
        ) {
            /*
            * Update the logo
            */
            viewModel.logoUrl =
                navBackStackEntry.savedStateHandle.get<String>(KEY_ACCOUNT_LOGO)!!

            navBackStackEntry.savedStateHandle.remove<AccountSort>(KEY_ACCOUNT_LOGO)
        }
    }
    navBackStackEntry.lifecycle.addObserver(observer)

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            navBackStackEntry.lifecycle.removeObserver(observer)
        }
    })
}

Can someone help me on why this error is occurring ?

Upvotes: 0

Views: 1669

Answers (1)

Ziem
Ziem

Reputation: 6697

titleScenario.onFragment { fragment ->
    Navigation.setViewNavController(fragment.requireView(), navController)
}

onFragment is called after the fragment has moved to the resumed state. It's too late as you are using NavController in onCreateView (according to the stack trace you posted).

To fix this problem, you need to set the TestNavHostController way sooner, just after fragment's view has been created:

val titleScenario = launchFragmentInContainer<AddAccountFragment>(
    Bundle(),
    themeResId = R.style.Locky_Theme
).also { fragment ->
    fragment.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner ->
        if (viewLifecycleOwner != null) {
            Navigation.setViewNavController(fragment.requireView(), navController)
        }
    }
}

To learn more, visit Test Navigation guide.

Upvotes: 3

Related Questions