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 it will be explained how this approach was used to automate Contact Synchronization tests:

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: 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: navigate to People app 
        mDevice.pressHome();
        UiObject conutactsApp = 
            mDevice.findObject(new UiSelector().text(PEOPLE_APP));
        conutactsApp.clickAndWaitForNewWindow();

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

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

        // Espresso: 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'
}

More information about Espresso and UI Automator, including real examples, you can find inside the Android Espresso Revealed book.

android espresso revealed