|
| 1 | +--- |
| 2 | + |
| 3 | +title: "Android Mobile Testing: Fragments" |
| 4 | +excerpt: "How to decrease the number of E2E tests and improve coverage with Fragments Testing" |
| 5 | +tags: Mobile Android testing |
| 6 | +authors: |
| 7 | +- Raman Koushyk |
| 8 | +header: |
| 9 | + teaser: /assets/images/post/espresso-logo.png |
| 10 | + teaser_alt: Fragments |
| 11 | +category: Software Quality Assurance |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## **What is Fragment testing on Android** |
| 16 | + |
| 17 | + |
| 18 | +Firstly, we need to know what Fragments in Android are. |
| 19 | + |
| 20 | +{% include |
| 21 | + components/figure.html |
| 22 | + url="/assets/images/post/containers.jpeg" |
| 23 | + description="Reusable containers" |
| 24 | +%} |
| 25 | + |
| 26 | + |
| 27 | +Fragments are reusable containers within our app allowing us to present the same user interface layout in a variety of activities and layout configurations. Given the versatility of fragments, it's important to validate that they provide a consistent and resource-efficient experience. |
| 28 | + |
| 29 | +Note the following: |
| 30 | +* Your fragment shouldn't be dependent on a specific parent activity or another fragment. |
| 31 | +* You shouldn't create a fragment's view hierarchy unless the fragment is visible to the user. |
| 32 | + |
| 33 | +To perform Fragments testing we have such helper as AndroidX "fragment-testing" library, which provides the "FragmentScenario" class to create Fragments and change their `Lifecycle.State`. We are able to launch the screen we want to test and skip the rest. |
| 34 | + |
| 35 | + |
| 36 | +## **Let's Start** |
| 37 | + |
| 38 | +## 0. Before |
| 39 | +I'm going to use our test Application to showcase. It's called Jukebox (and was created by our awesome android developer Kunal Jadhav). It's a simple application but ideal for a demo. Let's have a look at it. |
| 40 | + |
| 41 | +{% include |
| 42 | + components/figure.html |
| 43 | + url="/assets/images/post/E2E_test.gif" |
| 44 | + description="E2E Test" |
| 45 | +%} |
| 46 | + |
| 47 | +As you can see we have Login, Playing Now, Most Popular and More Menu Screens. |
| 48 | + |
| 49 | +For the demo I want to test Title on Most Popular Screen and check if it contains text according to the requirements or not. |
| 50 | + |
| 51 | +**Before**<br> |
| 52 | +In our usual approach(E2E) we would have written a test which would do the following: |
| 53 | +- Open the app |
| 54 | +- Enter Username and Password |
| 55 | +- Click Login button |
| 56 | +- Select Most Popular tab |
| 57 | +- Check Title |
| 58 | + |
| 59 | +**Now**<br> |
| 60 | +Fragments testing approach: |
| 61 | +- Open the app at Most Popular Screen in Fragment |
| 62 | +- Check Title |
| 63 | + |
| 64 | +What do we need to start using Fragments for testing? The First thing is Dependencies. |
| 65 | + |
| 66 | +## 1. Dependencies |
| 67 | + |
| 68 | +```kotlin |
| 69 | +dependencies { |
| 70 | + |
| 71 | + //Fragment testing library |
| 72 | + debugImplementation ('androidx.fragment:fragment-testing:1.5.3') |
| 73 | + |
| 74 | + //Espresso and test utilities for local and instrumentation tests |
| 75 | + testImplementation ('junit:junit:4.13.2') |
| 76 | + debugImplementation ('androidx.test:core:1.4.0') |
| 77 | + androidTestImplementation ('androidx.test:runner:1.4.0') |
| 78 | + androidTestImplementation ('androidx.test.ext:junit:1.1.3') |
| 79 | + androidTestImplementation ('androidx.test.ext:junit-ktx:1.1.2') |
| 80 | + androidTestImplementation ('androidx.test:rules:1.4.0') |
| 81 | + androidTestImplementation ('androidx.test.espresso:espresso-core:3.1.0') |
| 82 | + androidTestImplementation ('androidx.test.espresso:espresso-contrib:3.1.0') |
| 83 | + androidTestImplementation ('androidx.test.espresso:espresso-intents:3.1.0') |
| 84 | + androidTestImplementation ('androidx.test.ext:junit-ktx:1.1.2') |
| 85 | + androidTestImplementation ('androidx.test.uiautomator:uiautomator:2.2.0') |
| 86 | + androidTestUtil ('androidx.test:orchestrator:1.4.1') |
| 87 | + |
| 88 | +} |
| 89 | +``` |
| 90 | +NOTE: List of Dependencies(please, be aware of outdated versions) |
| 91 | + |
| 92 | +## 2. Coding |
| 93 | + |
| 94 | +As mentioned earlier, the “fragment-testing” library provides the FragmentScenario class to create fragments. The launchScreen method allows us to launch a fragment which contains a UI. Afterwards, your fragment will be attached to the Activity's root view. |
| 95 | + |
| 96 | +```kotlin |
| 97 | +/** |
| 98 | + * Launch a screen fragment with the given [fragmentArgs] but its optional. |
| 99 | + */ |
| 100 | +inline fun <reified S : Fragment> launchScreen( |
| 101 | + fragmentArgs: Bundle? = null, |
| 102 | + @StyleRes themeResId: Int = R.style.Theme_MyApplication) = launchFragmentInContainer<S>(fragmentArgs) |
| 103 | +``` |
| 104 | + |
| 105 | +We have launchScreen method, and we can go further. The next stop is our @Before the test part. |
| 106 | + |
| 107 | +```kotlin |
| 108 | +@Before |
| 109 | +override fun setUp() { |
| 110 | + mockedModule = module(override = true) {} |
| 111 | + super.setUp() |
| 112 | + launchScreen<com.bb.qa_assignment.journey.dashboard.mostPopular.MostPopularScreen>() |
| 113 | + mostPopularScreen = MostPopularScreen() |
| 114 | +} |
| 115 | +``` |
| 116 | +The first part can be our mocked modules. In some of our projects we use different mocks but its not required for this test project. We can see launchScreen method where we provide the Screen path we want to launch. |
| 117 | + |
| 118 | +```kotlin |
| 119 | +@Test |
| 120 | +fun testScreenContent() { |
| 121 | + mostPopularScreen.apply { |
| 122 | + mostPopularTitle.shouldHaveText(R.string.label_most_popular) |
| 123 | + } |
| 124 | +} |
| 125 | +``` |
| 126 | +In the test we check whether the title is there displaying the text specified in the requirements. |
| 127 | + |
| 128 | +## 3. Results |
| 129 | + |
| 130 | +<figure class="figure d-block text-center mb-4"> |
| 131 | + <img class="figure-img img-fluid" src="/assets/images/post/fragment_test.gif" style="max-width: 220px"> |
| 132 | + <figcaption class="figure-caption">Fragment Test</figcaption> |
| 133 | +</figure> |
| 134 | + |
| 135 | +Looks pretty nice (if you don't blink) :) |
| 136 | + |
| 137 | +## 4. Metrics |
| 138 | + |
| 139 | +Testing android Fragments in isolation gives us time to d̶r̶i̶n̶k̶ ̶c̶o̶f̶f̶e̶e̶. Seriously, the most important benefits of using such an approach is decreasing execution time and increasing coverage. Let’s compare some results. |
| 140 | + |
| 141 | +I've made the same test but as E2E. You can see the results below. |
| 142 | + |
| 143 | + |
| 144 | +{% include |
| 145 | + components/figure.html |
| 146 | + url="/assets/images/post/e2e_fragment_results.png" |
| 147 | +%} |
| 148 | + |
| 149 | +The same checks are done using the different approaches. Fragment test is more than 5 times faster, and we avoid possible fails during test execution. Sounds amazing. If we think about the scalability of these tests then the results are even more impressive. |
| 150 | +Just imagine that in your project there are 100 E2E tests (execution time of ~20 minutes based on my previous project) and then you replace half of them with Fragment tests. The same checks are performed but your execution time decreases to ~7-8 minutes. You get more stable and less flaky tests. |
| 151 | + |
| 152 | +## 5. Conclusion |
| 153 | + |
| 154 | +I would be really happy if that was the end. Sadly, there are some caveats you must be aware of. |
| 155 | + |
| 156 | +- Testing with Fragments has a close connection with the source code of an app. |
| 157 | + You will need to maintain your Fragment tests when some changes in source code occur. It's easier than maintaining E2E but in some cases might get tricky. |
| 158 | +- Limitations of testing Real API calls, Some specific logic, Navigation. |
| 159 | + You will not able to use Real API calls, all the data should be mocked. |
| 160 | + It's not possible to check cases if you need to change your Activity. |
| 161 | + It's possible to make a check for navigation but with limitations. It's still better to use E2E tests for that. |
| 162 | + |
| 163 | + |
| 164 | +## **Final Thoughts** |
| 165 | + |
| 166 | +Introducing Fragments tests won't replace your E2E tests. It should be a mix of E2E and testing with Fragments. That mix gives you the power that your previous automation tool never dreamed of. |
| 167 | + |
| 168 | +The most useful source: https://developer.android.com/guide/fragments/test |
| 169 | + |
| 170 | +Special thanks to: Maria Vlasova |
0 commit comments