问题
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 ?
回答1:
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.
来源:https://stackoverflow.com/questions/62515314/android-espresso-does-not-have-a-navcontroller-set-error