Android Espresso simple UI test


Example

UI testing tools

Two main tools that are nowadays mostly used for UI testing are Appium and Espresso.

AppiumEspresso
blackbox testwhite/gray box testing
what you see is what you can testcan change inner workings of the app and prepare it for testing, e.g. save some data to database or sharedpreferences before running the test
used mostly for integration end to end tests and entire user flowstesting the functionality of a screen and/or flow
can be abstracted so test written can be executed on iOS and AndroidAndroid Only
well supportedwell supported
supports parallel testing on multiple devices with selenium gridNot out of the box parallel testing, plugins like Spoon exists until true Google support comes out

How to add espresso to the project

dependencies {
  // Set this dependency so you can use Android JUnit Runner
  androidTestCompile 'com.android.support.test:runner:0.5'
  // Set this dependency to use JUnit 4 rules
  androidTestCompile 'com.android.support.test:rules:0.5'
  // Set this dependency to build and run Espresso tests
  androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
  // Set this dependency to build and run UI Automator tests
  androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.2.2'
}

NOTE If you are using latest support libraries, annotations etc. you need to exclude the older versions from espresso to avoid collisions:

    // there is a conflict with the test support library (see http://stackoverflow.com/questions/29857695)
    // so for now re exclude the support-annotations dependency from here to avoid clashes
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') {
        exclude group: 'com.android.support', module: 'support-annotations'
        exclude module: 'support-annotations'
        exclude module: 'recyclerview-v7'
        exclude module: 'support-v4'
        exclude module: 'support-v7'
    }
    // exclude a couple of more modules here because of <http://stackoverflow.com/questions/29216327> and
    // more specifically of <https://code.google.com/p/android-test-kit/issues/detail?id=139>
    // otherwise you'll receive weird crashes on devices and dex exceptions on emulators
    // Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
    androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') {
        exclude group: 'com.android.support', module: 'support-annotations'
        exclude group: 'com.android.support', module: 'design'
        exclude module: 'support-annotations'
        exclude module: 'recyclerview-v7'
        exclude module: 'support-v4'
        exclude module: 'support-v7'
    }
    //excluded specific packages due to https://code.google.com/p/android/issues/detail?id=183454
    androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.2') {
        exclude group: 'com.android.support', module: 'support-annotations'
        exclude module: 'support-annotations'
        exclude module: 'recyclerview-v7'
        exclude module: 'support-v4'
        exclude module: 'support-v7'
    }

    androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.2') {
        exclude group: 'com.android.support', module: 'support-annotations'
        exclude module: 'support-annotations'
        exclude module: 'recyclerview-v7'
        exclude module: 'support-v4'
        exclude module: 'support-v7'
    }

    androidTestCompile('com.android.support.test:runner:0.5') {
        exclude group: 'com.android.support', module: 'support-annotations'
        exclude module: 'support-annotations'
        exclude module: 'recyclerview-v7'
        exclude module: 'support-v4'
        exclude module: 'support-v7'
    }
    androidTestCompile('com.android.support.test:rules:0.5') {
        exclude group: 'com.android.support', module: 'support-annotations'
        exclude module: 'support-annotations'
        exclude module: 'recyclerview-v7'
        exclude module: 'support-v4'
        exclude module: 'support-v7'
    }

Other than these imports it is necessary to add android instrumentation test runner to build.gradle android.defaultConfig:

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

Device setup

For non flaky test it is recommended to set following settings on your devices:

  • Developer options / Disable Animations - reduces flakyness of tests
  • Developer options / Stay awake - if you have dedicated devices for tests this is usefull
  • Developer options / Logger buffer sizes - set to higher number if you run very big test suites on your phone
  • Accessibility / Touch & Hold delay - long to avoid problems with tapping in espresso

Quite a setup from the real world ha? Well now when thats out of the way lets take a look how to setup a small test

Writing the test

Lets assume that we have the following screen: Smple screen for espresso test The screen contains:

  • text input field - R.id.textEntry
  • button which shows snackbar with typed text when clicked - R.id.shownSnackbarBtn
  • snackbar which should contain user typed text - android.support.design.R.id.snackbar_text

Now lets create a class that will test our flow:

/**
* Testing of the snackbar activity.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
    //espresso rule which tells which activity to start
    @Rule
    public final ActivityTestRule<SnackbarActivity> mActivityRule = 
        new ActivityTestRule<>(SnackbarActivity.class, true, false);


    @Override
    public void tearDown() throws Exception {
        super.tearDown();
        //just an example how tear down should cleanup after itself
        mDatabase.clear();
        mSharedPrefs.clear();
    }
    
    @Override
    public void setUp() throws Exception {
        super.setUp();
        //setting up your application, for example if you need to have a user in shared
        //preferences to stay logged in you can do that for all tests in your setup
        User mUser = new User();
        mUser.setToken("randomToken");
    }
    
    /**
    *Test methods should always start with "testXYZ" and it is a good idea to 
    *name them after the intent what you want to test
    **/
    @Test
    public void testSnackbarIsShown() {
        //start our activity
        mActivityRule.launchActivity(null);
        //check is our text entry displayed and enter some text to it
        String textToType="new snackbar text";
        onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
        onView(withId(R.id.textEntry)).perform(typeText(textToType));
        //click the button to show the snackbar
        onView(withId(R.id.shownSnackbarBtn)).perform(click());
        //assert that a view with snackbar_id with text which we typed and is displayed
        onView(allOf(withId(android.support.design.R.id.snackbar_text), 
        withText(textToType))) .check(matches(isDisplayed()));
    }
}

As you noticed there are 3-4 things that you might notice come often:

onView(withXYZ) <-- viewMatchers with them you are able to find elements on screen

perform(click()) <-- viewActions, you can execute actions on elements you previously found

check(matches(isDisplayed())) <-- viewAssertions, checks you want to do on screens you previously found

All of these and many others can be found here: https://google.github.io/android-testing-support-library/docs/espresso/cheatsheet/index.html

Thats it, now you can run the test either with right clicking on the class name / test and selecting Run test or with command:

./gradlew connectedFLAVORNAMEAndroidTest