[MUSIC PLAYING]
SPEAKER: If your app uses background threads for loading
or processing data-- and pretty much every app will need those
at some point-- you might have to take special precautions
to make your UI test run reliably.
Espresso does most of the heavy lifting for you,
waiting for life cycle events to finish
and checking if the UI is idle.
But it can't always know what the other parts of your app
are doing.
And you might need to write some additional code to make
synchronization possible.
Let me show you an example.
I'll create a new project in Android Studio
with the empty activity template.
Let's do some initial setup for testing.
I'll add the Android runner, rules,
and Espresso dependencies in my Android test scope.
If you're ever unsure what the newest versions for Espresso
packages are, consult the documentation or our testing
samples on GitHub, which we always keep up to date.
I then set up the test instrumentation runner,
and we're done here.
Onto the main activity class.
The only thing it does right now is
set a layout for the text view.
Let me open the layout and change that text
and give the text view an ID.
And on resume, I'm going to change the text.
And I'll just quickly extract the string
into a resource for later re-use.
Let's create a UI test for the activity using an Android
Studio shortcut.
Add the annotation to use the Android test runner first.
Then I need to set up the activity test
rule that will start my activity and check if the correct text
is displayed.
I run the test, and it passes.
That's because Espresso waits until the app processes
all life cycle events.
And I'm sending it a text string on resume, remember?
Let's simulate a more realistic scenario
where the data takes some time to load.
AsyncTask is one of the simplest ways to do background work
and then pass a result back to the main thread.
So what happens when I run the test now?
Surprisingly, it passes.
That's because Espresso also knows
how to synchronize on the AsyncTask thread
pull out of the box.
And because AsyncTask are also at the heart of any AsyncTask
loader, including cursor loaders, they just work.
Of course, there's lots of other ways
you can schedule background work.
It can be connecting to a remote API, using a custom thread
pull, or through handler messaging.
You can see I'm changing the code
to use a separate thread, which sleeps
for 5 seconds to simulate background work,
then posts the change back to the main thread.
When I run the test, it fails immediately.
As soon as on resume returns and the activity is idle,
Espresso is trying to validate the message on the text view
and it fails because the runnable
hasn't had a chance to run yet.
That's when you have to use idling resources
to tell Espresso when your app is busy processing.
I'll use an implementation of an idling resource
that we provide with Espresso called
counting idling resource.
It's very simple to use, and it basically
lets you increment a counter for every resource that is busy
when you're waiting for something to finish
loading and decrement it when it's done.
Espresso will automatically run whenever
the counter is at zero.
I'll give it a name, data load, used mostly for logging.
But it has to be unique among registered idling resources.
Now, increment before scheduling my runnable
and decrement when it's done.
The only thing left is to register the resource
with Espresso.
In the test class, I'll add a setup method
to grab the idling resource instance from the activity.
And it's done.
Run test.
After 5 seconds, the counter gets decremented
and the test passes.
To sum up, if your tests are flaky or outright failing
because of background threads and asynchronous events,
use idling resource to tell Espresso when
to pause and resume its work.
That's all for this episode.
Happy testing.
[MUSIC PLAYING]