Espresso. Test your UI now!
Testing your app has always been a major milestone that everyone likes to achieve. There is no reason explaining the pros of testing your app, rather I will begin by explaining the types of tests in Android.
The above pyramid may look familiar to a lot of people. Let’s begin by examining each pyramid block one by one:
- Unit tests are the fastest and least expensive tests you can write. They should cover most if not all of your application’s logic (but not its dependencies).
- Integration tests validate how your code interacts with other parts of the system but without the added complexity of a UI framework.
- UI tests are the slowest and most expensive kind of test. They are slow because you must deploy your app to an emulator or device and drive it using the UI.
This post only explores the Espresso API to write UI tests. Now let’s begin by the setup procedure and finally writing your first Espresso test case!!
Setup Instructions
You can get your espresso setup in Android Studio buy adding just a few dependencies:
And that’s it, the API is ready to be used!
You might see the androidTest folder when you switch to project view, something like below
Don’t worry if you don’t see the androidTest folder, you can simply create one! Right click on src folder -> Create new directory and name it as androidTest
Inside your androidTest folder -> Create new directory and name it java
Now to begin writing your first test case, we will take the classic example of the login screen. The login scenario, in my case, is:
- when the user opens the app, he is directed to the app home screen, then
- using the login option provided in the navigation drawer, the login fragment is loaded (contained in a parent activity named WelcomeActivity)
- this fragment contains a email and password field with a login button at the bottom
- if the login details are correct (a network call is made which verifies the details) the user is redirected back to the home screen
I began writing the above test scenario and ended up writing something like below:
Although the test runs successfully, but there are a lot of concerns to look into. After writing this first test case, I realised the problems that might occur if I continue to write test cases like these!!
Problem 1: My app deals mostly in fragments rather than activities, So the init() can be a real pain in finding the parent activity and loading the required fragment in it.
Solution: Why not create a test activity, which can always be used to load the required fragment for testing? Let’s see how it can be done
So I created a TestFragmentActivity with 2 helper methods setFragment() and replaceFragment(). And I changed my test rule and init() methods as below:
Now if you were to ask me how this really helped? The objective is to test the screen, not the user flow!! So For instance, if your app has a login screen and, after login a list of items. Most projects, in order to test the list screen, would, first login and then test the list. This is what I call ‘user flow’. This example is very simple, but you can immediately see the issue here. The more screens you have, the more difficult it becomes to test all possible error scenarios in every screen while performing the user flows, and the less value your test(s) will carry as much of the code in the test, is there just to take you to the screen you want to test.
Problem 2: Obviously the thread.sleep(2000). This is absolutely not the way to wait for the login response from the server(or any network operation). Imagine the server returns the response in 2.1sec, then you will wonder how your test failed and all digging through the logs is just a waste of time!!
Solution: Fortunately espresso provides the Idling Resource to overcome this. The API is really simple, it is just meant to perform an increment whenever a network call starts, and perform a decrement whenever a network call is finished.
So let’s see how we can use the Idling Resource to encounter this problem.
Firstly we need to add the dependency to our app gradle as follows:
The we create a class EspressoIdlingResource in our main app package
Then finally we use this IdlingResource in our FragmentLogin class like below,
Pretty simple, isn’t it? You just need call increment whenever you make a network call, and decrement whenever your network call finishes! However if you are using AsyncTask instead, you don’t need to worry about anythig, Espresso supports AsyncTask and it automatically waits for the task to finish before it can begin testing the UI.
Now after tackling the above problems, I ended up with a test case written below
There are other scenarios too which can be quite complicated while performing the UI test (especially in the case of RecyclerView). I will talk about the other test scenarios dealing with Recycler View in the second part of this tutorial.
Till then you can play around with your app and think of test cases, since testing is always fun!!
Reading material :
Must watch: