Fragment Not Associated With A Fragment Manager

28,846

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.

Share:
28,846

Related videos on Youtube

Alex Kombo
Author by

Alex Kombo

Updated on December 13, 2021

Comments

  • Alex Kombo
    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
      Moinkhan about 5 years
      can you post nav_graph xml ?
    • Alex Kombo
      Alex Kombo about 5 years
      @Moinkhan please check updated question with nav_graph
    • raj kavadia
      raj kavadia about 5 years
      Have you migrated the project from appcompat to androidx ? if that's the case change the fragment manager of appcompat to android x.
    • The VOYOU
      The VOYOU about 5 years
      can you rerun your test case again after commenting Handler().postDelayed({ if (user == null) findNavController().navigate(R.id.action_splashFragment_to_l‌​oginFragment) //navigate to login screen if no user exists else findNavController().navigate(R.id.action_splashFragment_to_b‌​usinessListFragment) //navigate to business list if user already logged in }, 2000)
    • Alex Kombo
      Alex Kombo about 5 years
      @rajkavadia Yes, I'm using AndroidX. However, using the Navigation Component means I needn't use the FragmentManager.
    • Alex Kombo
      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
      Mayank Saini almost 5 years
      @AlexKombo Were you able to solve this one, I have a similar case
    • Alex Kombo
      Alex Kombo almost 5 years
      @MayankSaini Yes. You can either use Thread.sleep() or an IdlingResource.
  • StayCool
    StayCool almost 4 years
    I 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
    StayCool over 3 years
    I mean, I wait, then check if I am still on that fragment, and only then navigate
  • JaydeepW
    JaydeepW about 3 years
    Yes, this helped me and looks like a cleaner approach than postDelayed.
  • Kashan Ahmad
    Kashan Ahmad almost 3 years
    That was perfect. Flawless Victory my friend.