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>
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.