Fragment Not Associated With A Fragment Manager
Solution 1
I found out that in my case the issue was related to threading. I solved it with:
lifecycleScope.launchWhenResumed {
findNavController().navigate(R.id.action_splashFragment_to_loginFragment)
}
Would be good to know if this help you?!
Solution 2
Comment below code of yours
Handler().postDelayed({
if (user == null)
findNavController().navigate(R.id.action_splashFragment_to_loginFragment)
//navigate to login screen if no user exists
else
findNavController().navigate(R.id.action_splashFragment_to_businessListFragment)
//navigate to business list if user already logged in
}, 2000)
If it works fine then
use Thread.sleep(5000)
or IdlingResource
to make a delay in your instrumentation test cases.
Root Cause: Actually, during your test case runtime, you are trying to operate upon the fragment which is waiting on Handler for 2000 millies to execute.
Related videos on Youtube
Alex Kombo
Updated on December 13, 2021Comments
-
Alex Kombo over 2 years
I am writing some instrumented tests for an app with one activity and multiple fragments using the
Navigation Component
.The code for my splash screen is as below:
class SplashFragment : Fragment(), KodeinAware { override val kodein by Admin.instance.kodein private var realm: Realm? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.splash, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) (activity as? AppCompatActivity)?.supportActionBar?.hide() realm = Realm.getInstance(RealmUtil.realmConfig) val result = realm!!.where<User>().findFirst() val user = if (result != null) realm!!.copyFromRealm(result) else null Handler().postDelayed({ if (user == null) findNavController().navigate(R.id.action_splashFragment_to_loginFragment) //navigate to login screen if no user exists else findNavController().navigate(R.id.action_splashFragment_to_businessListFragment) //navigate to business list if user already logged in }, 2000) } override fun onDestroy() { super.onDestroy() realm?.close() } }
I am attempting to test a fragment that comes after the splash screen but I keep getting the following error:
java.lang.IllegalStateException: Fragment SplashFragment{a1ca381 (5f5b98ae-c130-4e9b-9b77-0495561ef4f5)} not associated with a fragment manager. at androidx.fragment.app.Fragment.requireFragmentManager(Fragment.java:891) at androidx.navigation.fragment.NavHostFragment.findNavController(NavHostFragment.java:106) at androidx.navigation.fragment.FragmentKt.findNavController(Fragment.kt:29) at com.chargebot.collect.admin.fragment.onboarding.SplashFragment$onViewCreated$1.run(SplashFragment.kt:44) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at androidx.test.espresso.base.Interrogator.loopAndInterrogate(Interrogator.java:148) at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:519) at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:478) at androidx.test.espresso.base.UiControllerImpl.injectKeyEvent(UiControllerImpl.java:201) at androidx.test.espresso.base.UiControllerImpl.injectString(UiControllerImpl.java:357) at androidx.test.espresso.action.TypeTextAction.perform(TypeTextAction.java:108) at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:360) at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:251) at androidx.test.espresso.ViewInteraction.access$100(ViewInteraction.java:64) at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:157) at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:154) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
My test class is as follows:
@RunWith(AndroidJUnit4::class) @MediumTest class SignInTest { @get: Rule val login = ActivityScenarioRule(Home::class.java) @Test fun loginWithoutEmail_ShouldDisplayError() { val scenario = launchFragmentInContainer<LoginFragment>() onView(withId(R.id.password)).perform(typeText("samplePassword"), closeSoftKeyboard()) onView(withId(R.id.login)).perform(click()) onView(withId(com.google.android.material.R.id.snackbar_text)).check(matches(withText(R.string.enter_email))) } @Test fun loginWithoutPassword_ShouldDisplayError() { val scenario = launchFragmentInContainer<LoginFragment>() onView(withId(R.id.email)).perform(typeText("[email protected]"), closeSoftKeyboard()) onView(withId(R.id.login)).perform(click()) onView(withId(com.google.android.material.R.id.snackbar_text)).check(matches(withText(R.string.enter_password))) } }
None of my test functions will execute because of the aforementioned error. What's the cause of the exception since tests I run on the Splash screen run successfully?
My
nav_graph
is as below:<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph" app:startDestination="@id/splashFragment"> <fragment android:id="@+id/splashFragment" android:name="com.chargebot.collect.admin.fragment.onboarding.SplashFragment" android:label="SplashFragment" tools:layout="@layout/splash"> <action android:id="@+id/action_splashFragment_to_loginFragment" app:destination="@id/loginFragment" app:popUpTo="@+id/splashFragment" app:popUpToInclusive="true" /> <action android:id="@+id/action_splashFragment_to_businessListFragment" app:destination="@id/businessListFragment" app:popUpTo="@+id/splashFragment" app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/registrationFragment" android:name="com.chargebot.collect.admin.fragment.onboarding.RegistrationFragment" android:label="RegistrationFragment" tools:layout="@layout/registration"> <action android:id="@+id/action_registrationFragment_to_businessListFragment" app:destination="@id/businessListFragment" app:popUpTo="@+id/registrationFragment" app:popUpToInclusive="true" /> <action android:id="@+id/action_registrationFragment_to_loginFragment" app:destination="@id/loginFragment" app:popUpTo="@+id/registrationFragment" app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/loginFragment" android:name="com.chargebot.collect.admin.fragment.onboarding.LoginFragment" android:label="LoginFragment" tools:layout="@layout/login"> <action android:id="@+id/action_loginFragment_to_businessListFragment" app:destination="@id/businessListFragment" app:popUpTo="@+id/loginFragment" app:popUpToInclusive="true" /> <action android:id="@+id/action_loginFragment_to_registrationFragment" app:destination="@id/registrationFragment" app:popUpTo="@+id/loginFragment" app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/businessListFragment" android:name="com.chargebot.collect.admin.fragment.business.BusinessListFragment" android:label="BusinessListFragment" tools:layout="@layout/businesses_list"> <action android:id="@+id/action_businessListFragment_to_newBusinessFragment" app:destination="@id/newBusinessFragment" /> <action android:id="@+id/action_businessListFragment_to_branchesFragment" app:destination="@id/branchesFragment" /> </fragment> <fragment android:id="@+id/newBusinessFragment" android:name="com.chargebot.collect.admin.fragment.business.NewBusinessFragment" android:label="NewBusinessFragment" tools:layout="@layout/add_business_layout"> <action android:id="@+id/action_newBusinessFragment_to_businessListFragment" app:destination="@id/businessListFragment" /> </fragment> <fragment android:id="@+id/branchesFragment" android:name="com.chargebot.collect.admin.fragment.branch.BranchesFragment" android:label="BranchesFragment" tools:layout="@layout/branches_layout"> <action android:id="@+id/action_branchesFragment_to_transactionsFragment" app:destination="@id/transactionsFragment" /> <action android:id="@+id/action_branchesFragment_to_newBranchFragment" app:destination="@id/newBranchFragment" /> <action android:id="@+id/action_branchesFragment_to_collectorsFragment" app:destination="@id/collectorsFragment" /> </fragment> <fragment android:id="@+id/transactionsFragment" android:name="com.chargebot.collect.admin.fragment.transactions.TransactionsFragment" android:label="TransactionsFragment" tools:layout="@layout/transactions" /> <fragment android:id="@+id/newBranchFragment" android:name="com.chargebot.collect.admin.fragment.branch.NewBranchFragment" android:label="NewBranchFragment" tools:layout="@layout/add_branch_layout" /> <fragment android:id="@+id/collectorsFragment" android:name="com.chargebot.collect.admin.fragment.collector.CollectorsFragment" android:label="CollectorsFragment" tools:layout="@layout/client_list_layout"> <action android:id="@+id/action_collectorsFragment_to_newCollectorFragment" app:destination="@id/newCollectorFragment" /> <action android:id="@+id/action_collectorsFragment_to_transactionsFragment" app:destination="@id/transactionsFragment" /> <action android:id="@+id/action_collectorsFragment_to_collectorTransactions" app:destination="@id/collectorTransactions" /> </fragment> <fragment android:id="@+id/newCollectorFragment" android:name="com.chargebot.collect.admin.fragment.collector.NewCollectorFragment" android:label="NewCollectorFragment" tools:layout="@layout/new_client_layout" /> <fragment android:id="@+id/collectorTransactions" android:name="com.chargebot.collect.admin.fragment.transactions.CollectorTransactionsFragment" android:label="CollectorTransactions" tools:layout="@layout/transaction_layout"> <action android:id="@+id/action_collectorTransactions_to_collectorsFragment" app:destination="@id/collectorsFragment" app:popUpTo="@id/collectorsFragment" /> </fragment> </navigation>
-
Moinkhan about 5 yearscan you post nav_graph xml ?
-
Alex Kombo about 5 years@Moinkhan please check updated question with
nav_graph
-
raj kavadia about 5 yearsHave you migrated the project from appcompat to androidx ? if that's the case change the fragment manager of appcompat to android x.
-
The VOYOU about 5 yearscan you rerun your test case again after commenting
Handler().postDelayed({ if (user == null) findNavController().navigate(R.id.action_splashFragment_to_loginFragment) //navigate to login screen if no user exists else findNavController().navigate(R.id.action_splashFragment_to_businessListFragment) //navigate to business list if user already logged in }, 2000)
-
Alex Kombo about 5 years@rajkavadia Yes, I'm using
AndroidX
. However, using theNavigation Component
means I needn't use theFragmentManager
. -
Alex Kombo about 5 years@TheVOYOU running it without the
Handler
works just fine but my use case requires there to be a delay before the next fragment is loaded. -
Mayank Saini almost 5 years@AlexKombo Were you able to solve this one, I have a similar case
-
Alex Kombo almost 5 years@MayankSaini Yes. You can either use
Thread.sleep()
or anIdlingResource
.
-
-
StayCool almost 4 yearsI think your assumption is wrong. My NavController fails with same error: "Fragment Not Associated With A Fragment Manager" when I try to wait for 2 sec in corotuineScope and then findNavContoller.navigate somewhere
-
StayCool over 3 yearsI mean, I wait, then check if I am still on that fragment, and only then navigate
-
JaydeepW about 3 yearsYes, this helped me and looks like a cleaner approach than postDelayed.
-
Kashan Ahmad almost 3 yearsThat was perfect. Flawless Victory my friend.