Jun 26 2019

Hello there! My name is Divyansh Jain. I am a student of Mahatma Jyoti Rao Phoole University. I have been selected as the Android developer for the XWiki organization and I am currently working on their XWiki Android Authenticator application in GSOC19. In the last week, I wrote a blog post titled "Reasons to choose Kotlin". This week, I will discuss Android UI Unit Testing with Espresso.

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 so that we can focus on that individual unit only. As test runs, we compare the test results with expected behavior which helps us to identify even a single flaw too.

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 allows us to replicate the user UI interaction while testing. So when we run, say login test on an emulator, we replicate the user entering its credentials and see whether his credentials are correct or not. 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 interactions that the user does on the UI which is very simple to use. For example:

  • Let’s say that you want to enter the username in the edittext on the screen, then 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 known as the 'viewMatcher'. So if the view exists, then it performs the 'viewAction'. Here viewAction -  "perform(typeText("TestUser"))" writes the text “TestUser” in the field.

  • You can also put check() which verifies the content of the given view. Say you want to check the text written on the button or check if textView content has changed or not, in that case, we run:

    onView(withId(R.id.tvExample)).check(matches(withText("Hello")))

    Here onView function is the 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, then the test will pass, otherwise, it will 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.

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 are loading the data in the RecyclerView/ListView. What happens now? 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 the amount of 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 that you have to wait for it. Then as soon as the operation is completed, it loads the data and displays it.

Conclusion

I really enjoyed working on UI testing. Espresso made all the work very easy. viewMatcher (onView(objectID)) easily finds the object in the current view, and to simulate user inputs, viewActions is equipped with all kinds of viewActions (perform(actionName)). While working, I did stumble upon an error (androidx.test.espresso.PerformException) which states "Error performing "viewAction" on view 'Animations or transitions are enabled on the target device." To resolve this, go to system setting -> Developer Option -> set all three animations to "No animation". That will do the trick. I did enjoy working with it, I am sure, you will too. Feel free to ask for any doubts.

Happy Reading.

Tags:
Created by Divyansh Jain on 2019/06/26
    

Get Connected