Android UI Unit Testing with Espresso
Unit testing is a way to test individual units/components of the app. It helps to break down the app into individual units/components which helps us to focus on that individual unit only. As test runs, we analyze the test results with expected behavior which helps us to identify even a single flaw too. For this post, I choose the UI testing.
UI Testing: The basic concept of the UI testing is the same as Unit Testing, but here we run UI components like login, signup, etc. on Emulator or test device. This way is followed to replicate the user UI interaction while testing. So when we run say login test, on emulator we replicate the user entering its credentials and see whether his credentials are correct or incorrect. Here two major things are used:
- Espresso: Here interaction is done by Espresso. Espresso helps us to simulate the user interaction with UI. It can typeText, clear text, click on the button, scroll to a list, swipe left, swipe right, etc. In summary, it can perform all user interaction that user do on the UI which is very simple to use. For example:
- let’s say that you want to enter username in the edittext on the screen, I’ll execute:
onView(withId(R.id.accountName).perform(typeText("TestUser"))
Here onView(withId(R.id.accountName) is compulsory as it checks for the view (R.id.accountName) on the current view also called as viewMatcher. So if the view exists it perform the viewAction. Here viewAction perform(typeText("TestUser")) is to perform writing the text “TestUser” in the field. - You can also put check () which verifies the content of the given view. Say you want to check then on button click the the textView content has changed or not, in that case, we run:
onView(withId(R.id.tvExample)).check(matches(withText("Hello")))
Here onView functions same as in the above example. And next check method is a viewInteraction which matches the current view i.e tvExample with the text “Hello”. So if the text is matched test will pass otherwise not. - It can also check for the data loading into the Adapter. Espresso provides onData () method. By using onData () we force our desired element into the view hierarchy. Say in your adapter there’s an item “Coffee” of string type.
onData(allOf(`is`(instanceOf(String::class.java))
,`is`("Coffee"))).perform(click())
Earlier we were using onView() to find the view, but it cannot find the view that has not been loaded yet, so here we use onData() to match the string “Coffee” into the list.
- let’s say that you want to enter username in the edittext on the screen, I’ll execute:
- Idling Resource: As the Android Developers Documentation states: “An idling resource represents an asynchronous operation whose results affect subsequent operations in a UI test.” By using Idling Resource, we can notify Espresso to wait for the completion of Asynchronous operation and then execute the rest of the code. Let’s assume that there are cases where you’re loading the data in the RecyclerView/ListView, what happened? Yes, it will not wait for the completion of the Asynchronous operation it will end the test case immediately. You may find some simple solutions like putting SystemClock.sleep(period) or Thread.sleep(period) but these are not good practices as you cannot determine that how much time it will take for your Asynchronous operation to complete, load the data and display it.
That’s where Idling resource comes to rescue. It notifies the Espresso that an Asynchronous operation is made and you’ve to wait for it. Then as soon as the operation is completed, loaded the data and displayed it.