Hello, espresso! Part 2 Working with lists

Gaurav Singh
6 min readMay 7, 2022

--

In the previous post Hello, espresso! Part 1 Introducing you to the world of espresso automation!

We read an introduction to espresso, and understood how to write a basic test flow in espresso. We also saw the structure of an espresso test

In this post, we’ll dive into how to automate tests and work with list like components using espresso. Examples of such components are AdapterView, RecyclerView etc

Let’s go! 🏃🏃‍♀️

Working with lists in general

Using espresso, we can scroll to or perform an action on a given item in a list

We need this since sometimes the view you are interested in may not be present on the screen and espresso needs to scroll to it based on some matcher.

There are some nuances to this though:

  • Android lists are created with RecyclerView only a small no of child elements are created and they are recycled as you scroll. For these use cases, we can useonView with RecyclerViewActions
  • AdapterView has data backed by an Adapter, for this we use onData with a custom matcher based on the how the adapter is designed

For these use cases scrollTo the method won't work since that needs an existing view

Let’s see an example and walk through how we could use espresso to scroll and act on elements for these types of components

Working with AdapterView

We’ll use DataAdapterSample for this post. You can find the app and test code at this path in my forked Github repo

Understanding the app under test

Once you load the project in android studio and let gradle build it, you can run the app and see it load up on the emulator

Let’s understand the flow we want to automate a bit better

  • we have a ListView component where each row has a TextView and aToggleButton
  • You can scroll in this list
  • Once you tap on a row, there is a LinearLayout at the top that's set with the row no

Assume that we want to automate this flow

Using Layout Inspector to figure out the app structure

You don’t need to dive into the source code to figure out how everything would render on the UI when the app is built

Android studio provides a convenient tool called Layout Inspector, that allows us to inspect the Component Tree. (Kind of similar to Appium Inspector) You can open it by going to Tools > Layout Inspector, You’ll also need to select the process you want to inspect (In this case you can select DataAdapterSample)

Open layout inspector

Understanding the UI

The layout inspector has 3 main sections:

  1. Component Tree: Here we can see the tree like structure that makes up our current screen, we can observe theListView with TextView and Button and the staticLinearLayout on top
  2. Live App: This section is refreshed as you interact with your app in the emulator, you can select any particular row and it would highlight the same in Component Tree and also show the attributes
  3. Attributes: Here we can see all the attributes or properties of the given element and use these while automating our tests in espresso

Writing our test for AdapterView

Let’s write our tests to perform the actions mentioned above

We’ll use a series of learning tests to explore how to test different aspects of this app and test and also learn espresso’s API a bit better, in a real test you may just write one test to perform the workflow you intend to test.

You can see the complete test file here with some helpful comments explaining what each test is supposed to do

To start we want to create the usual structure i.e. Write our class DataAdapterPractice with @RunWith annotation and use ActivityScenarioRule to start our LongListActivity activity

Before we try scrolling, it may be helpful to verify that the item that we want to scroll to (say a TextView with text: item: 99) does not exist, we can do that with below line:

Here is the complete test:

Next, we want to be able to scroll to the last item with text item: 99, we can figure out this text by actually scrolling in the app and then seeing the value of text attribute in the layout inspector

If we see the code for LongListActivity we can see that the ListView gets its data from an adapter LongListAdapter that has a hash map keys like ROW_TEXT and ROW_ENABLED

We can use this understanding to write our matcher to find this row

  • To scroll to such an element we use onData instead of onView (since the element is not displayed on the view)
  • In our test, We want to find the element whose ROW_TEXT is item:99 and we can do so usinghasEntry Hamcrest matcher that is able to match elements in a hash map
  • Espresso would take care of automatically scrolling to this element for us
  • We can then close the flow by checking that such a row is visible by using below ViewAssertion

Below is how the complete test looks like:

Click on a row and verify the LinearLayout has expected test

If the user taps on a particular element in the ListView then the app updates the row no in a separate TextView with id: selection_row_value

We can repeat similar steps to scroll to the element with text value 30, tap on it and then check if the TextView at the top is updated with the correct value

To click on a child element inside a ListView we can use onChildView() method like below:

Below is how the complete test looks like:

Working with RecyclerView

RecyclerView is a different type of list which ensures when the user scrolls off a screen, it recycles elements in an efficient manner. We do not use onData in this case. To understand how RecyclerView works you could read this post on Android developers

To work with RecyclerView, we can use espresso-contrib package in our app's gradle dependencies

The dependency supports below actions:

  • scrollTo() - Scroll to matched view
  • scrollToHolder() - Scroll to matched view holder
  • scrollToPosition() - Scroll to specific position

Performing action on element

  • actionOnItem() - Perform view action on matched view
  • actionOnHolderItem() - Perform view action on a matched View holder
  • actionOnItemAtPosition() - Perform a view action on a view at a specific position

App under test

For these tests, we’ll use RecyclerViewSample test app

In this test app, we have a RecyclerView where each row is a TextView having text like This is element #42, as the user scrolls the same elements are recycled and reused by Android framework

Learning tests

Let’s write some tests for this app

Test that a given element is not present in the list and espresso throws an exception

We’ll start with a negative test, what if we try to scroll to an element that does not exist, we should expect espresso framework to throw an exception in this case, this test also is a good way to demo the scrollTo method in RecyclerViewActions

Below is th how the complete test looks like:

Let’s say we want to scroll into our list to the 40th item and click on it, we use actionOnItemAtPosition method to specify the position and also add the click() method to click that

We can then check if the item with expected text is displayed using below:

Below is how the complete test looks like:

Our app has a special row in the middle with a text: This is the middle!, let's say we want to scroll to this elements holder and then verify its displayed

We can use scrollToHolder and pass it a custom matcher to verify if it has reached the middle

Let’s see how isInTheMiddle() method is implemented

We create a new TypeSafeMatcher of type CustomAdapter.ViewHolder which overridden implementation of matchesSafely that returns getIsInTheMiddle() methods output. Note: The support for this method is added in apps source code

To see the app impl supporting this, see CustomAdapter class that has a method onBindViewHolder which sets this flag in viewHolder.setIsInTheMiddle(true); if the position == mDataSet.size() / 2

Finally, we can check if the view is displayed using:

Please see the complete test below:

Further reads

You can read Espresso lists on Android developers

Conclusion

Hopefully this post gives you an idea on how to work with list like components in espresso. Stay tuned for next post where we’ll dive into how to automate and work with intents with espresso

As always, Do share this with your friends or colleagues and if you have thoughts or feedback, I’d be more than happy to chat over at twitter or comments. Until next time. Happy Testing and coding.

Originally published at https://automationhacks.io on May 7, 2022.

--

--

Gaurav Singh
Gaurav Singh

Written by Gaurav Singh

Principal SDET @ CRED, ex Meta, Gojek | 13+ years of hands-on solving deep testing problems and leading engineering teams

No responses yet