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>
FragmentManager is the class responsible for performing actions on your app's fragments, such as adding, removing, or replacing them, and adding them to the back stack.
This class was deprecated in API level 28. Use the Support Library androidx.
Another way to simply add a Fragment to FragmentContainerView is by using the attribute android:name=”fragment_class_name_path" in XML. Both the attributes android:name or class are similar things we just need to give the classpath as a value to inflate the fragment.
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?!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With