Transcripted Summary

In this chapter we will discuss Synchronization with Espresso using idling resources, how they work and how they are used with our tests.


Idling Resources

Idling resources represent asynchronous operations whose results can affect subsequent operations in a UI test.

By registering idling resources with Espresso, you can validate these asynchronous operations more reliably when testing your application.

In order to make Espresso aware of your app's long-running operations you must register each one as an idling resource.

Espresso first checks a condition to perform any action on the view, verifies the current asynchronous tasks, verifies the state of idling resources (i.e. whether or not they are idle) and verifies the state of the message queue with any running threads.



Looking at the state diagram we see that the flow of logic must wait until the application is idle before Espresso can proceed with any test-specific actions.

By default, there are two conditions to check when determining whether or not an application is idle in Espresso:

  1. Whether or not there is something in the UI thread message queue.

  2. Whether or not there are any asynchronous tasks running in the background.

If the application is performing a long-running task in the background without using an asynchronous task, Espresso generally won't know about it and this could cause subsequent test executions to fail in unexpected ways.


Using Idling Resources (Demo)

In this demo we will learn how to use idling resources with our test cases.

Begin by running the IdlingResourceSample sample application and explore the interface.

You’ll notice that this application is nearly identical to the BasicSample project from Chapter 3 and includes much of the same source code aside from some modifications made for this demo.



If we type "hello TAU" into the text box and click on the "CHANGE TEXT TAKING SOME TIME" button there is now a noticeable delay with the text "Waiting for message…" appearing before the label text updates.

This delay will need to be handled by idling resources.



If we expand the project package on the left side of our IDE we will find the folder for idling resources as well as "MainActivity" and "MessageDelayer".

MessageDelayer contains the method processMessage() which handles message processing with idling resources and is otherwise waiting for messages.

Under the "IdlingResources" folder we have the "SimpleIdlingResource" class which implements various maintenance methods.

The MainActivity class is responsible for returning existing resource objects or creating (instantiating) new ones as necessary.

Before our test begins we need to register any idling resources.

After our test ends we need to unregister these same resources so that memory can be freed up.

In this demo we will use the CountingIdlingResources class from the espresso-contrib package.

This is typically used with multiple threads to maintain a count of pending operations.

Our test involves one or more pending operations waiting for a text message to display.

We need to add some additional dependencies to our "build.gradle" file for idling resources because they are not included in the core Espresso dependencies.

Your exact dependencies may vary depending on whether or not you have migrated to the newer "androidx" package structure.


androidTestImplementation "androidx.test.espresso:espresso-contrib:3.2.0"
implementation "androidx.test.espresso.espresso-idling-resource:3.3.0-beta01"

Now we are ready to begin using idling resources in our test.

There is already a test case changeText_sameActivity() implemented in this demo project but if we attempt to run it now it will fail.

The problem is that it is trying to assert a verification string "Hello TAU" against the label text which (due to the delay) is actually set to "Waiting for message…" at the time of assertion.

We need to fix this test and make it work using our idling resources.


Let’s register our idle resources before running the test using the annotation @Before.


    private IdlingResource myIdling;

    @Before
    public void registerIdlingResources()
    {
        myIdling = activityTestRule.getActivity().getIdlingResource();
        IdlingRegistry.getInstance().register(myIdling);
    }

Now that our idling resources are registered the application will wait if any idling resources are found in the queue.

If not, the application logic will continue normally.

Don’t forget to unregister our idling resources afterwards to remove all waits from the application.

Use the @After annotation for this:


    @After
    public void unRegisterIdlingResources()
    {
        if (myIdling != null){
            IdlingRegistry.getInstance().unregister(myIdling);
        }
    }

Now our application is ready to handle idling resources before the test begins and will unregister those same resources on test completion.

Let's run our test again and check the results.

The test now passes because our use of counting idling resources ensures that application execution will wait until either the idling resources finish execution or all of the application elements become ready.


Code


package com.example.android.testing.espresso.IdlingResourceSample;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import androidx.test.espresso.IdlingRegistry;
import androidx.test.espresso.IdlingResource;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.ActivityTestRule;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class ChangeTextTest {

    @Rule public ActivityTestRule<MainActivity> activityTestRule
        = new ActivityTestRule<>(MainActivity.class);

    private IdlingResource myIdling ;

    @Before
    public void registerIdlingResources()
    {
        myIdling = activityTestRule.getActivity().getIdlingResource();
        IdlingRegistry.getInstance().register(myIdling);
    }

    @After
    public void unRegisterIdlingResources()
    {
        if (myIdling != null){
            IdlingRegistry.getInstance().unregister(myIdling);
        }
    }

    @Test
    public void changeText_sameActivity() {
        onView(withId(R.id.editTextUserInput))
            .perform(typeText("Hello TAU"));
        closeSoftKeyboard();
        onView(withId(R.id.changeTextBt))
            .perform(click());
        onView(withId(R.id.textToBeChanged))
            .check(matches(withText("Hello TAU")));
    }
}


Resources



© 2024 Applitools. All rights reserved. Terms and Conditions Privacy Policy GDPR