Android Espresso: How do I test a specific Fragment when following one activity to several fragment architecture
Solution 1
Espresso can test Fragments
only if they are displayed. And that requires them to be displayed by an Activity
.
With your current setup you'll have to use Espresso to click()
your way (like a user would) to the Fragment
you actually want to test.
In one of my projects I have a ViewPager
that displays Fragments
. For those Fragments
I use a custom FragmentTestRule
to test them in isolation. I can start each Fragment
directly and use Espresso to test it. See this answer.
You could also:
- Do not use
Fragments
.Activities
are easier to test. You can test eachActivity
on its own. In most casesFragments
offer no advantage overActivities
.Fragments
just make the implementation and testing more difficult. - Enable your
FragmentActivity
to directly show a certainFragment
when it is created. E.g. by supplying a special intent extra to yourFragmentActivity
. But this would add testing code to your app, which is generally not a good solution.
Solution 2
If you are using the Navigation Architecture component, you can test each fragment instantly by Deep linking to the target fragment (with appropriate arguments) at the beginning of the test.
@Rule
@JvmField
var activityRule = ActivityTestRule(MainActivity::class.java)
protected fun launchFragment(destinationId: Int,
argBundle: Bundle? = null) {
val launchFragmentIntent = buildLaunchFragmentIntent(destinationId, argBundle)
activityRule.launchActivity(launchFragmentIntent)
}
private fun buildLaunchFragmentIntent(destinationId: Int, argBundle: Bundle?): Intent =
NavDeepLinkBuilder(InstrumentationRegistry.getInstrumentation().targetContext)
.setGraph(R.navigation.navigation)
.setComponentName(MainActivity::class.java)
.setDestination(destinationId)
.setArguments(argBundle)
.createTaskStackBuilder().intents[0]
destinationId being the fragment destination id in the navigation graph. Here is an example of a call that would be done once you are ready to launch the fragment:
launchFragment(R.id.target_fragment, targetBundle())
private fun targetBundle(): Bundle? {
val bundle = Bundle()
bundle.putString(ARGUMENT_ID, "Argument needed by fragment")
return bundle
}
Also answered in more detail here: https://stackoverflow.com/a/55203154/2125351
Solution 3
So, according to patterns and recommended practices by several companies. You need to write targeted and hermetic test for each view, whether it is activity, fragment, dialog fragment or custom views.
First you need to import the following libs into your project through gradle if you are using gradle in the following way
debugImplementation 'androidx.fragment:fragment-testing:1.2.0-rc03'
debugImplementation 'androidx.test:core:1.3.0-alpha03'
For Kotlin, debugImplementation 'androidx.test:core-ktx:1.3.0-alpha03'
In order to test fragment independently from activity, you can start/launch it in the following way:
@Test
fun sampleTesting(){
launchFragmentInContainer<YourFragment>()
onView(withId(R.id.sample_view_id)).perform(click())
}
This way, you can independently test your fragment from your activity and it is also one of recommended way to achieve hermetic and targeted ui test. For full details, you can read the fragment testing documentation from android docs
https://developer.android.com/training/basics/fragments/testing
And I found this repository with lots of test example useful even though it is extremely limited to complexity of test cases with super simple tests. Though it can be a guide to start.
https://github.com/android/testing-samples
Related videos on Youtube
Ersen Osman
Updated on September 26, 2021Comments
-
Ersen Osman over 2 years
My app consists of one
Activity
for manyFragments
.I wish to use Espresso to test the UI of the
Fragments
. However I ran into a problem.How can I test a
Fragment
which is not added to anActivity
inonCreate
. All examples I have seen withFragments
involve theFragment
being added inonCreate
. So how can I tell Espresso to go to a specificFragment
and start from there?Thanks
-
Brais Gabin over 6 yearsFragmentTestRule it's an implematation of the linked answer.
-
OldSchool4664 over 6 yearsFragments load faster than activities (they can even be pre-loaded into memory), they allow for modularization and reuse of components, and aren't required to be displayed full screen (they can be placed in a layout just like any other view). Fragments are very popular because they improve upon an Activity a lot. Saying Fragments offer no advantage is incredibly inaccurate, and the benefit of using them far outweighs the testing difficulty.
-
Pawan almost 6 yearsI am Sorry, but "Not using Fragments", is not a solution. Fragments are damn reusable & faster as compared to Activities.
-
thaussma almost 6 yearsYes they are useful. And this is just a suggestion. If you don't know how to test them, maybe try a testable approach... I know how to test them and I use them too.
-
StefanTo over 5 yearsI didn't look into it exactly, maybe it takes some time for the FragmentManager to commit the new state, maybe it was just an animation that you could disable easily on test devices... however I think it'd be overengineered to create an idling resource for FragmentManager in a test code snippet... Or how would your solution look like?
-
Drocchio over 5 yearsI suggest you remove (if you like) the fragment suggestion, or to specify that is your favourite preference, so that users can upvote you as `I just did, and in the future juniors reading your reply will understand that the first part of your response was perfectly valid