web-dev-qa-db-fra.com

Fragment non associé à un gestionnaire de fragments

J'écris des tests instrumentés pour une application avec une activité et plusieurs fragments en utilisant le Navigation Component.

Le code de mon écran de démarrage est le suivant:

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()
    }
}

J'essaie de tester un fragment qui vient après l'écran de démarrage mais j'obtiens toujours l'erreur suivante:

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)

Ma classe de test est la suivante:

@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)))
    }
}

Aucune de mes fonctions de test ne s'exécutera en raison de l'erreur susmentionnée. Quelle est la cause de l'exception, car les tests que je lance sur l'écran Splash s'exécutent correctement?

Ma nav_graph est comme ci-dessous:

<?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>
14
Alex Kombo

Commentaire ci-dessous code de la vôtre

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)

Si ça marche bien alors

utilisez Thread.sleep(5000) ou IdlingResource pour retarder vos cas de test d'instrumentation.

Cause principale: En fait, pendant l'exécution de votre scénario de test, vous essayez d'opérer sur le fragment qui attend le gestionnaire pendant 2000 millièmes pour s'exécuter.

1
The VOYOU