Easy Instrumented Tests (UI Tests) for Android in 2021

Christopher Elias
ProAndroidDev
Published in
6 min readMay 20, 2021

--

Photo by Oğuzhan Akdoğan on Unsplash

In the previous post, we’ve got familiarized with unit testing. Now is the turn for test our UI’s.

What are Instrumented tests? 🤔

Instrumented tests are tests that run on physical devices and emulators, and they can take advantage of the Android framework APIs and supporting APIs, such as AndroidX Test.

Official documentation.

Our goal with instrumented tests is to verify the “actions” that the user can perform like clicking a button, writing on a EditText, or verify if some views are visible or not on a given view state.

Once we have our unit tests running successfully we know that our methods works as expected, but what about our views?… Are they rendering or behaving as expected? How can we be sure that the views are correctly displayed or the buttons are calling the right methods after a onClick event?

Well, that’s exactly why Instrumented tests exist. With the help of libraries like espresso, fragment tests, etc. we can launch our activities or fragments into a emulator or a physical phone and validate the things we mention before.

We are going to run an Instrumented test on this screen and verify if the views are displaying correctly according to the view state model.

Actors list fragment.

Set up 🛠️

From my point of view, this could be the part that it’s most difficult for us because we usually get libraries conflicts, some weird gradle errors and didn’t even reach to run our UI tests. It is important to see that in this case, the project im working on is using Koin as service locator in order to provide the dependencies my classes need.

If you are using something else like Dagger — Dagger Hilt probably you’ll need to change or add some other dependencies in order to set up your UI tests. After the set up part, everything remains the same. (Dagger hilt has good documentation and the library is getting better and better, Im planning to migrate from koin to hilt and write about it).

Let’s start by adding the following dependencies to our build.gradle file

// The versions listed here are the ones at the time im writing this blog.

dependencies {
//... Other dependenciesandroidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"androidTestImplementation "io.mockk:mockk-android:1.11.0"androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.0"androidTestImplementation "io.insert-koin:koin-test-junit4:3.0.2"debugImplementation "androidx.fragment:fragment-testing:1.3.3"}

Now, inside the android block in the same file, add the following

android {
...

defaultConfig {
...

testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
... testOptions {
animationsDisabled = true
}
...

packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
exclude 'META-INF/LGPL2.1'
exclude 'META-INF/AL2.0'
exclude("META-INF/*.kotlin_module")
}
}

In the android.defaultConfig block add the testInstrumentationRunner for be able to “launch” our tests, in the case you use a custom application class for your tests you must implement your own instrumentation runner and override that value for the package location of your custom instrumentation runner.

The android.testOptions will disable the animations if any in order to avoid some other error types. Finally, in the android.packagingOptions block exclude the files from above, in order to avoid weird compilations errors.

Code time 🎧💻💥

A quick intro first. We know how our screen look based on the screenshot from avobe. In this “feature” Im using MVVM (check this blog if you wan’t to know how). Let’s focus on the ActorsListFragment class and see what’s going on…

ActorsListFragment.

Very straight forward isn’t it? We render the whole UI in just one place! That’s great! because our tests are going to hit exactly that “place”, the renderUiState method.

Let’s go to our src/androidTest directory and create a ActorsListFragmentInstrumentedTest class. It will look something like this

ActorsListFragmentInstrumentedTest

The first step is to annotate the class with the RunWith(AndroidJUnit4::class) annotation. We are testing a fragment, how can we launch a fragment without an activity? Thankfully the new fragment testing library comes with helpers for do this, FragmentScenario allow us to launch a fragment in a incorporated container. If you want to see how does it work, check out this great video! Im sure it’s going to be useful for you

The video is just 12 mins and it is definitely worth it.

Our main task here is to validate if our views are behaving as we expect when a given state enters to the renderUiState method, therefore, we don’t need to work with our ViewModel and all the classes it depends directly! Remember the phrase from the previous blog?

As long as your code is clean, your tests are going to be really easy to make

Time to instantiate our variables and prepare our environment, like this

We first create a lateinit var instance of a FragmentScenario in order to launch our ActorListFragment in the fragment scenario container (activity). Our fragment, has a dependency on a ViewModel, but we are not going to actually use it, therefore we can just mock it.

Our test is extending AutoCloseKoinTest in order to load & unload koin modules automatically.

Now we are going to code the actual tests, like this

We use the fragmentScenario.onFragment block and invoke the renderUiState method with a custom view State model.

After passing the custom state model that is going to render UI, we are validating our views visibility work as we expect with the help of the espresso methods.

That’s it. Now you can run the test and it will run on a Emulator or your phone if is connected.

We’ve got the whole actor list screen tested from start to end now. We know that our repository works as we expected, we know that our views are being draw correctly when a given state is passed.

Surprise tip 🎁

Sometimes you will need to share some classes between your unit & instrumented tests. If that is your case, please follow the instructions on this blog in order to create a shared test directory. After doing what the blog says everything inside that directory can be available for your unit & instrumented tests.

Conclusion 🍾

It wasn’t so hard to test and validate if our screen is working as we expect. With the help of unit & instrumented tests running successfully we can be sure that our app is going to have less or none side effect / bug.

So you save valuable time, increase your product effectiveness and have less customers angry for something that don’t work!

Go and explore the espresso or other libraries and tests your forms, lists, navigation flows & more.

The project can be found here

Don’t forget to leave your claps, stars, comments, improvements, jokes etc! If you want to ask me something DM me and I will get you back as soon as I can.

See you later 👋.

--

--