Espresso – UI Testing Framework for Android

Testing your app is an integral part of the app development process. By running tests against your app consistently, you can verify your app’s correctness, functional behavior, and usability before you release it publicly.

Espresso is a testing Framework for writing automated UI – user interface tests for your Android application. With espresso, we can simulate the user interaction in our application, but instead a real user is using the app, with espresso we will automate the process by writing code in Java or Kotlin language.
It has been developed by Google and it allows both black-box testing as well as testing of individual components during development cycles.
Automated testing is an integral part of the development lifecycle.

The main components of Espresso are:

  • Espresso – Entry point to interactions with views (via onView() and onData()).
  • ViewMatchers – A collection of objects that implement the Matcher<? super View> interface. You can pass one or more of these to the onView() method to locate a view within the current view hierarchy.
  • ViewActions – A collection of ViewAction objects that can be passed to the ViewInteraction.perform() method, such as click().
  • ViewAssertions – A collection of ViewAssertion objects that can be passed the ViewInteraction.check() method. Most of the time, you will use the matches assertion, which uses a View matcher to assert the state of the currently selected view.
onView(withId(R.id.my_view))        // withId(R.id.my_view) is a ViewMatcher
    .perform(click())               // click() is a ViewAction
    .check(matches(isDisplayed()))  // matches(isDisplayed()) is a ViewAssertion

In Espresso equivalent to findViewById() is onView(withId(R.id.my_view)). If you want to narrow down your search, combination matches can be used: onView(allOf(withId(R.id.my_view), withText("Hello!"))) or onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
If the target view is inside an AdapterView, such as ListView, GridView, or Spinner, the onView() method might not work. In these cases, you should use onData() instead.

When we have found a suitable matcher for the target view, it is possible to perform instances of ViewAction on it using the perform method.

For example, to click on the view we can use: onView(...).perform(click()) or if we want to execute more than one action with one perform call: onView(...).perform(typeText("Hello"), click())

If the view we are working with is located inside a ScrollView (vertical or horizontal), to ensures that the view is displayed before proceeding to the other action we have to use: scrollTo(): onView(...).perform(scrollTo(), click())

Assertions can be applied to the currently selected view with the check() method. The most used assertion is the matches() assertion. It uses a ViewMatcher object to assert the state of the currently selected view.

For example, to check that a view has the text "Hello!" use: onView(...).check(matches(withText("Hello!")))

If you want to assert that a view with the text "Hello!" is visible – for example after a change of the views visibility flag you can use:
onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))

Example Click on the button test:
onView(withId(R.id.button_simple)).perform(click())

To verify the TextView text you can use:
onView(withId(R.id.text_simple))
.check(matches(withText("Hello Espresso!")))

AdapterView is a special type of widget that loads its data dynamically from an Adapter.
The most common example of an AdapterView is ListView. As opposed to static widgets like LinearLayout, only a subset of the AdapterView children may be loaded into the current view hierarchy. A simple onView() search would not find views that are not currently loaded.

Espresso handles this by providing a separate onData() entry point which is able to first load the adapter item in question, bringing it into focus prior to operating on it or any of its children.

In example adapter test we have to open the Spinner, select a specific item, and verify that the TextView contains the item. As the Spinner class is based onAdapterView, it is recommended to use onData() instead of onView() for matching the item:
Open the item selection:
onView(withId(R.id.spinner_simple)).perform(click())
Select an item:
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Verify that text is correct:
onView(withId(R.id.spinnertext_simple))
.check(matches(withText(containsString( "Americano"))))

Espresso logs all view actions to logcat. 
Espresso warns users about presence of AdapterView widgets. When an onView() operation throws a NoMatchingViewException and AdapterView widgets are present in the view hierarchy, the most common solution is to use onData(). The exception message will include a warning with a list of the adapter views. You may use this information to invoke onData() to load the target view.

Espresso setup in Android Studio

Add the following lines inside dependencies of your app’s build.gradle file:

androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0')
androidTestImplementation('androidx.test:runner:1.4.0')
androidTestImplementation('androidx.test:rules:1.4.0')

In the same build.gradle add:

defaultConfig {
   ...
   testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

Android Studio creates tests by default in src/androidTest/java/com.example.package/

To create a test configuration in Android Studio, complete the following steps:

  1. Open Run > Edit Configurations.
  2. Add a new Android Tests configuration.
  3. Choose a module.
  4. Add a specific instrumentation runner: androidx.test.runner.AndroidJUnitRunner
  5. Run the newly created configuration.

The Espresso Cheat Sheet contains most available instances of MatcherViewAction, and ViewAssertion:

For more detailed information releted to app testing you can visit: https://developer.android.com/training/testing