Hello Folks, This is my second article on Android Instrumentation testing. My previous instrumentation testing article was about testing a list view with core instrumentation (without any external library or framework). In this I am going to discuss the Espresso framework which I use for android instrumentation testing. I have also recored a series of videos on instrumentation testing in Android.

What is Espresso?

Espresso is a framework used for Android Instrumentation Testing. It provides a lots of useful APIs to test the UI elements and simulate the user interaction on the application. Espresso takes care of waiting for the main thread to be idle and perform the actions at the appropriate time. With this we will not have to put hacks like Thread.sleep in the code.  Espresso can run on devices with Android 2.2  or higher (API 8 or higher).

Espresso Matchers

Espresso has two core APIs

  1. To access the view elements or verify the view elements.
  2. To perform an action on the view.

For Example below code verifies that element is present on the view

onView(withId(R.id.items_list)).check(matches(isDisplayed()))
onView(withId(R.id.items_list)).check(matches(withChild(withText("Item 1"))))

Below is an example of swiping up the screen and clicking an item on the view

onView(withId(R.id.parent_layout)).perform(swipeUp());
onView(withId(R.id.submit_button)).perform(click());


Setup Espresso

Below is the build config of my demo project

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "ajitsingh.com.instrumentationtestdemo"
        minSdkVersion 21
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    packagingOptions {
        exclude 'LICENSE.txt'
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:support-annotations:22.2.0'

    androidTestCompile 'com.android.support.test:rules:0.3'
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2') {
        exclude group: 'com.android.support', module: 'support-annotations'
    }
    androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2') {
        exclude group: 'com.android.support', module: 'support-annotations'
    }
}


Example

I have created a demo project which has one test class which uses espresso and another with core instrumentation

@RunWith(AndroidJUnit4.class)
public class ItemListActivityEspressoTest {

  @Rule
  public ActivityTestRule<ItemListActivity> main = new ActivityTestRule<ItemListActivity>(ItemListActivity.class){
    @Override
    protected void beforeActivityLaunched() {
      Intents.init();
      super.beforeActivityLaunched();
    }

    @Override
    protected void afterActivityFinished() {
      super.afterActivityFinished();
      Intents.release();
    }
  };

  @Test
  public void shouldLaunchTheMainActivityAndFindItemsInTheList() throws Exception {
    onView(withId(R.id.items_list)).check(matches(withChild(withText("Item 1"))));
    onView(withId(R.id.items_list)).check(matches(withChild(withText("Item 2"))));
    onView(withId(R.id.items_list)).check(matches(withChild(withText("Item 3"))));
    onView(withId(R.id.items_list)).check(matches(withChild(withText("Item 4"))));
  }

  @Test
  public void shouldShowTheItemDetailWhenAnItemIsClicked() throws Exception {
    intending(hasComponent(ItemDetailActivity.class.getName()))
      .respondWith(new Instrumentation.ActivityResult(Activity.RESULT_OK, new Intent()));

    onView(withText("Item 1")).perform(click());
    intended(hasComponent(ItemDetailActivity.class.getName()));
  }
}