Skip to main content

Espresso & UIAutomator - the perfect tandem

Espresso for Android is perfect and fast test automation framework, but it has one important limitation - you are allowed to operate only inside your app under test context.

That means that it is not possible to automate tests for such app features like:

  • application push notifications
  • contact synchronization
  • navigating from another app to your app under test,

since you have to deal with other apps from the mobile device - Notification Bar, Contacts or People app, etc. 

In fact it wasn't possible until the release of UIAutomator 2.0. As stated in Android Developers blog post - "...Most importantly, UI Automator is now based on Android Instrumentation...".  And because of that we can run UIAutomator tests as well as Espresso tests using Instrumentation test runner.

In addition to that we can combine UIAutomator tests together with Espresso tests and this gives us the real power and control over the phone and application under test.

In the below example I'll explain  how this approach was used to automate Contact Synchronization tests for the app I'm working with:

import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;

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

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static junit.framework.Assert.assertTrue;

@RunWith(AndroidJUnit4.class)
public class TestContactsSync {

    @Rule
    public ActivityTestRule mActivityRule = new ActivityTestRule(LoginActivity.class);

    UiDevice mDevice;
    String PEOPLE_APP = "People";
    String MY_APP = "XING";
    String userContactName = "Android Tester";

    @Before
    public void setUp() throws Exception{
        super.setUp();
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    }

    @Test
    public void testContactsSync() throws Exception {

        // Espresso code: login the user, navigate to contact sync and enable clicking on toggle
        logInUser();
        onView(withText(R.string.sync_button)).perform(click());
        onView(withId(R.id.contacts_sync_enable_toggle)).perform(click());

        // UIAutomator code: navigate to People app 
        mDevice.pressHome();
        UiObject conutactsApp = mDevice.findObject(new UiSelector().text(PEOPLE_APP));
        conutactsApp.clickAndWaitForNewWindow();

        // UIAutomator code: check that contact is present in it after sync was triggered    
        UiObject contactName = mDevice.findObject(new UiSelector().text(userContactName));
        assertTrue(contactName.exists());

        // UIAutomator code: navigate back to my app under testm
        Device.pressHome();
        UiObject myApp = mDevice.findObject(new UiSelector().text(MY_APP));
        myApp.clickAndWaitForNewWindow();

        // Espresso code: turn off contact sync and logout
        onView(withId(R.id.contacts_sync_enable_toggle)).perform(click());
        onView(withId(R.id.logout)).perform(click());
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }
}
Isn't it perfect?! And the answer is - almost. UIAutomator requires Android 4.3 (API level 18) or higher. And this can be solved easily by tweaking build.gradle file - adding productFlavors and declare uiautomator dependencies:
productFlavors {
        // The actual application flavor 
        production {
            minSdkVersion 14
        }
        // Test application flavor for uiautomatior tests
        test {
            minSdkVersion 18
        }
    }

dependencies {
    // Instrumentation tests
    androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.1'
    androidTestCompile 'com.android.support.test:rules:0.2'
    // Set this dependency to build and run UI Automator tests
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.0.0'
}
Good luck :)

Comments

Popular posts from this blog

Discovering Espresso for Android: matching and asserting view with text.

After more than a month of using great test tool from Google - Espresso for Android, I'd like to share with you some of my experience. I assume that you've already added espresso jar into your project, spent some time playing with Espresso samples and have basic understanding how this tool works. In this post I'll show how to match particular view with text or assert that it contains (or not) specified Strings. Before we start, you have to take a look at Hamcrest matchers - Hamcrest tutorial and API Reference Documentation , which are used together with Espresso's ViewAssertions and ViewMatchers and included into Espresso standalone library. Pay more attention to Matcher<java.lang.String> matchers. So, here we go. For simplicity following String "XXYYZZ" will be used as a expected text pattern. Espresso ViewMatchers class implements two String matcher methods withText() and withContentDescription() which will match a view which text is equal

Discovering Espresso for Android: creating custom Matchers.

Hi! This time I'll talk about custom Matchers that can be used with Espresso for Android (and not only). We'll go through steps how to create them and I'll provide you examples of already existing and very useful ones. First of all a couple of words about Hamcrest library , which provides us with common matchers and possibility to create custom matchers, to pay tribute to it's authors. From Humcrest project main page - Hamcrest provides a library of matcher objects (also known as constraints or predicates) allowing 'match' rules to be defined declaratively, to be used in other frameworks. Typical scenarios include testing frameworks, mocking libraries and UI validation rules. In one of my previous post I've described already how to use some custom Hamcrest matchers. The base idea is that matcher is initialized with the expected values, which are compared against the actual object we are matching when invoking it. Among of the common matchers you

Preparing android emulator for UI test automation.

This post is about setting up android emulator for UI test automation. Properly configured emulator is the basis for reliable tests. Hundreds or thousands of professionally written test cases is great but if they become flaky because of the environment they are running on, their value reduces a lot. I will give you a couple of advices I'm following in my test automation projects. In general we will go through below topics: Managing emulator system animations Controlling soft keyboard appearance Changing emulator system locale Tweaking first and second points will reduce to minimum flakiness in our automated tests which can be caused by emulator. For those who are lazy to read the whole article at the bottom of the post I shared youtube video where I describe the same points with one more additional hint on top :) 1. There are three types of system animation we may control: window animation scale transition animation scale animator duration scale Emulator