Android Espresso: How do I test a specific Fragment when following one activity to several fragment architecture

后端 未结 4 660
刺人心
刺人心 2021-02-12 10:52

My app consists of one Activity for many Fragments.

I wish to use Espresso to test the UI of the Fragments. However I ran into a p

相关标签:
4条回答
  • 2021-02-12 11:19

    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'
    

    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

    0 讨论(0)
  • 2021-02-12 11:19

    Just show the Fragment using the Activity's SupportFragmentManager.

    For example (Kotlin) with ActivityTestRule:

    @Rule
    @JvmField
    var activityRule = ActivityTestRule(MainActivity::class.java)
    

    Just do this before your tests:

    @Before
    fun setup() {
        activityRule.activity.supportFragmentManager.beginTransaction().replace(R.id.main_activity_container_for_your_fragments, FragmentToShow(), "fragment-tag").commitAllowingStateLoss()
        Thread.sleep(500)
    }
    
    0 讨论(0)
  • 2021-02-12 11:20

    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 each Activity on its own. In most cases Fragments offer no advantage over Activities. Fragments just make the implementation and testing more difficult.
    • Enable your FragmentActivity to directly show a certain Fragment when it is created. E.g. by supplying a special intent extra to your FragmentActivity. But this would add testing code to your app, which is generally not a good solution.
    0 讨论(0)
  • 2021-02-12 11:42

    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

    0 讨论(0)
提交回复
热议问题