One of the key benefits of the Espresso framework is its test robustness. It is achieved through automatic synchronization of most of the test actions. Espresso waits for the main application UI thread while it is busy and releases test actions after the UI thread becomes idle. Moreover, it also waits for AsyncTask operations to complete before it moves to the next test step. In this chapter, we will see how Espresso can handle network operations using the IdlingResource mechanism and become familiar with the ConditionWatcher mechanism as an alternative to IdlingResource.
IdlingResource Basics
The message queue is empty.
There are no instances of AsyncTask currently executing a task.
All developer-defined idling resources are idle.
By performing these checks, Espresso substantially increases the likelihood that only one UI action or assertion can occur at any given time. This capability gives you more reliable and dependable test results.
However, it is not possible in every case to rely on automatic synchronization, for instance when the application being tested executes network calls via ThreadPoolExecutor. In order to let Espresso handle these kinds of long-lasting asynchronous operations, the IdlingResource must be created and registered before the test is executed.
It is important to register IdlingResource when these operations update the application UI you would like to further validate.
Performing network calls.
Establishing database connections.
CountingIdlingResource —Maintains a counter of active tasks. When the counter is zero, the associated resource is considered idle. This functionality closely resembles that of a semaphore. In most cases, this implementation is sufficient for managing your app’s asynchronous work during testing.
UriIdlingResource —Similar to CountingIdlingResource, but the counter needs to be zero for a specific period of time before the resource is considered idle. This additional waiting period takes consecutive network requests into account, where an app in your thread might make a new request immediately after receiving a response to a previous request.
IdlingThreadPoolExecutor —A custom implementation of ThreadPoolExecutor that keeps track of the total number of running tasks within the created thread pools. This class uses a CountingIdlingResource to maintain the counter of active tasks.
IdlingScheduledThreadPoolExecutor—A custom implementation of ScheduledThreadPoolExecutor . It provides the same functionality and capabilities as the IdlingThreadPoolExecutor class, but it can also keep track of tasks that are scheduled for the future or are scheduled to execute periodically.
To start using an idling resource mechanism in an application, the following dependency must be added to the application buid.gradle file (dependencies are mentioned for the Android Support and AndroidX Libraries).
These idling resource types use CountingIdlingResource in their implementation, so we will focus on CountingIdlingResource as a reference.
getName()—Returns the name of the resources.
Note
The IdlingResource name is represented by a String class and is used when logging, and for registration/unregistration purposes. Therefore, the name of the resource should be unique.
isIdleNow()—Returns true if the resource is currently idle. Espresso will always call this method from the main thread; therefore, it should be non-blocking and return immediately.
registerIdleTransitionCallback()—Registers the given resource callback with the idling resource. The registered callback is then used in the isIdleNow() method.
Note
The IdlingResource class contains a ResourceCallback interface that is used in the registerTransitionCallback() method. Whenever the application is going to switch states from busy to idle, the callback.onTransitionToIdle() method should be called to notify Espresso about it.
CountingIdlingResource is an implementation of IdlingResource that determines idleness by maintaining an internal counter. When the counter is zero, it is considered to be idle; when it is non-zero, it is not idle. This is very similar to the way a java.util.concurrent.Semaphore behaves.
The counter may be incremented or decremented from any thread. If it reaches an illogical state (like a counter that’s less than zero), it will throw an IllegalStateException. This class can then be used to wrap operations that, while in progress, block tests from accessing the UI.
Writing the Code
Running the First Test
IdlingResource should be registered before usage. IdlingRegistry handles registering and unregistering IdlingResource.
So, at this moment the CountingIdlingResource mechanism should be clear. This example described the way that we handle long-lasting or asynchronous actions of the application being tested. It is important to be careful with such idling resources and not to lock them during the test execution.
OkHttp3IdlingResource
Another idling resource sample that we look at is the OkHttp3IdlingResource . Why we should specifically look at it? OkHttp is one of the most used HTTP client libraries. It was developed by Square and used in a lot of Android applications. Probably because of this one, Square developer Jake Wharton implemented and open sourced this resource. See https://github.com/JakeWharton/okhttp-idling-resource . Here is how it looks.
- 1.
Add a dependency in the build.gradle file:
- 2.
In your test code, obtain the OkHttpClient instance and create an idling resource:
- 3.
Register the idling resource in the test code before running any Espresso tests:
By the way, don’t use the deprecated Espresso.registerIdlingResources() method ; instead use the IdlingRegistry implementation shown in this section.
Picasso IdlingResource
Picasso is the most popular image-downloading library for Android, which means it is a perfect candidate for another type of IdlingResource. The image-download idling resource can be used when we want to ensure that the whole application window layout is loaded together with the graphics. This can be extremely important in cases where graphical resources should be verified in tests. Here is the example of the PicassoIdling resource that’s also implemented in the androidTest/com.squareup.picasso package.
Note
The reason that the Picasso IdlingResource is in a separate package is because of the visibility of the targetToAction variable in the Picasso class, which is package protected.
ConditionWatcher as an Alternative to IdlingResource
As you may notice, the IdlingResource implementation is not trivial and requires continuous control over registering and unregistering. It is also not convenient to use IdlingResource in deep UI tests when a specific activity instance is needed to make it work.
As an alternative, you can try the ConditionWatcher class from AzimoLabs ( https://github.com/AzimoLabs/ConditionWatcher ). It is simple class that makes Android automation testing easier, faster, cleaner, and more intuitive. It synchronizes operations that might occur on any thread, with the test thread. ConditionWatcher can be used as a replacement to Espresso’s IdlingResources or it can work in parallel with them.
This is how it works: ConditionWatcher receives an instance of the Instruction class that contains a logical expression. Tests are paused until the moment the condition returns true. After that, the tests are immediately released. If the condition is not met within a specified timeout, the exception will be thrown and the test will fail.
setWatchInterval() — Sets the interval for periodic check of the logical expression. By default, it is set to 250 milliseconds.
setTimeoutLimit() — Sets the timeout for the ConditionWatcher to wait for a true value from the checkCondition() method. By default, it is set to 60 seconds.
waitForCondition() — Takes instructions containing a logical expression as a parameter and calls its checkCondition() method with the currently set interval, until it returns value true or until the timeout is reached. During that time, the test code won’t proceed to the next line. If timeout is reached, an Exception is thrown.
checkCondition() — A core method that’s equivalent to isIdleNow() of IdlingResource. It’s a logical expression and its changes, along with the monitored dynamic resource status, should be implemented there.
getDescription() — A string returned along with the timeout exception. The test author can include helpful information for the test crash debugging process.
setDataContainer() and getDataContainer() —A bundle that can be added to the Instruction class to share primitive types (e.g., a universal instruction that waits for any kind of view to become visible can be created, and resId could be sent via the bundle).
Or just copy the source code of the two ConditionWatcher.java and Instruction.java classes into your test source code.
Now let’s move to more complicated examples. In our sample application, we have a nasty snackbar that pops up every time a new TO-DO is added. It doesn’t allow us to add multiple TO-DOs to our list without waiting until it disappears. Our task is to create a watcher that will wait for the snackbar view to be gone. This is how it can be done.
ConditionWatchers can be extremely helpful when we have to wait for the different view states, but we should not overuse them in terms of waiting time. A problem can occur in cases when we may wait too much for a specific state of the view to be reached. When this waiting time becomes too long, it can seem like an issue with the application being tested and it is better to raise a bug than handle it inside your tests. Ideally, in most situations, IdlingResources should handle the majority of time the application is not idle, so ConditionWatchers should be a small addition to the waiting mechanism and be used occasionally, like in our snackbar case.
Exercise 12
- 1.
Implement a test that opens the menu drawer and navigates to another section. In this test, add a condition watcher that waits for a menu drawer to be shown or hidden. Use ViewMatchers.isDisplayed() for the shown state and hamcrest CoreMatchers.not(ViewMatchers.isDisplayed()) for hidden.
- 2.
Implement a waitForElement() ConditionWatcher that can be used with the DataInteraction type. Use the ViewInteraction waitForElement() function as a reference.
Making Condition Watchers Part of Espresso Kotlin DSL
Chapter 3 explained the Espresso Kotlin DSL as an example of much cleaner and compact test code. As you may notice, in the current implementation, all the functions from the ConditionWatchers class are not yet ready to be used in a similar way.
The thing is ConditionWatchers, as well as other Espresso methods, are implemented and executed in the same place and at the same time as the test code, which is the opposite to how IdlingResources are used—by registering them before the test run (usually in the @Before method).
ConditionWatchers.waitForElement():
ConditionWatchers.waitForElementFullyVisible():
ConditionWatchers.waitForElementIsGone():
All of these examples have the ViewInteraction return type and can be chained to Espresso test code as follows.
Exercise 13
- 1.
Implement a test that opens a menu drawer and navigates to another section. In this test, add a condition watcher that waits for the menu drawer to be shown or hidden. Use ViewMatchers.isDisplayed() for the shown state and hamcrest CoreMatchers.not(ViewMatchers.isDisplayed()) for hidden.
- 2.
Make the DataInteraction waitForElement() function in the previous task part of the DSL.
Summary
Properly handling network operations and asynchronous actions is a must-have in your UI tests. Applying IdlingResource or ConditionWatcher makes your UI tests much more stable and reliable. After using them at least once, it will be clear that there is no need to use explicit Thread.sleep() methods all over the tests, which is a bad practice and error-prone.