Why Espresso?

Why not? I’m serious. There’s so much about it that makes it a delight to use:

  • Hamcrest Matching
  • Simple syntax
  • Easy Extensibility

I toiled around a lot with the native Android instrumentation library before taking to Espresso and, I have to say, the difference is substantial.

Don’t put it off like I did, try it out now.

The Jekyll Logo

Prepping Your Tools

This tutorial assumes you’re using Android Studio. If you’re not, you should go download it or convince your boss it’s 10,000 times better than Eclipse (it’s free!).

Make Sure the Android Support Library’s Installed

By default, Android Studio will come with some version of the Android support library. What it doesn’t come with it, is the support repository. You can download/update both from the SDK Manager.

From the menu bar, click Tools, then hover over Android.

From the sub-menu, select SDK Manager. You’ll see an Android Studio dialog, but just go ahead and select Launch the standalone SDK Manager.

Scroll down to the bottom, under Extras. You’ll want to make sure the following are checked,

support libs in sdk manager.

Then, click Install Packages. Accept the licenses, and so on.

Check That You Have an androidTest Folder

As of Android Studio 1.5, new projects will be created with three src/ folders:

  • androidTest
  • main
  • test

You want to make sure these exist at <pathToYourProject>/<nameOfProject>/src/.

Your Espresso tests are considered instrumentation tests, so they go into the androidTest/ folder.

In Android Studio 2.+, both the androidTest and test packages will be visible at the same time (nice)

Add Gradle Dependencies

Now that your SDK has the support packages, you can just straight up add them to gradle as dependencies.

In your module level build.gradle, add the following to the dependencies clause,

  dependencies {
    ...
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    androidTestCompile 'com.android.support:support-annotations:23.1.1'
    androidTestCompile 'com.android.support.test:runner:0.4.1'
    androidTestCompile 'com.android.support.test:rules:0.4.1'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
    androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2.1') {
        exclude group: 'com.android.support', module: 'appcompat'
        exclude group: 'com.android.support', module: 'support-v4'
        exclude module: 'recyclerview-v7'
    }
  }

To run Espresso tests, you use the AndroidJUnitRunner as your instrumentation runner. Add this in the same build.gradle

  defaultConfig {
    ...
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  }

And that’s it! You can now write Espresso tests. Let’s not dally.

Brewing Your First Shot

So, I’m not going to be posting the layout XML and Activity here, but you can view them along with the test classes in this gist. Essentially, we’re writing tests for an Activity with a RecyclerView, EditText, and FloatingActionButton.

Creating Your Test Class

All Espresso test classes must extend ActivityInstrumentationTestCase2<T> where T specifies the Activity you’re testing. The super constructor takes in a ClassName object.

The @RunWith(AndroidJUnit4.class) annotation tells the compiler to build the test class against the JUnit4 library.

  @RunWith(AndroidJUnit4.class)
  public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity> {

    public MyActivityTest() {
      super( MyActivity.class );
    }

  }

Then, in order to make sure your test is properly injected and you get a reference to the Activity instance, override the setUp method. Here, you’ll want to keep a reference to the Activity instance.

  @RunWith(AndroidJUnit4.class)
  public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity> {

    public MyActivityTest() {
      super( MyActivity.class );
    }

    @Before
    @Override
    protected void setUp() throws Exception {
        super.setUp();

        injectInstrumentation( InstrumentationRegistry.getInstrumentation() );
        mActivity = getActivity();
    }
  }

Writing the Simplest Espresso Test

So, if you tried to run the code above, you’ll see it fail and tell you that you have no test cases. Let’s fix that.

All test methods can be specified with the @Test annotation, or have the name prefixed by test.

In your test class, add the following method.

  @Test
  public void testFAB() {

  }

This is the barebones of a runnable test class. You can try to run your test by right-clicking it in the project view and selecting Run ‘MyActivityTest’.

It should say something like Done: 1 of 1 SUCCESS (if everything was setup correctly).

Make Your Test Do Something

Now, let’s fill up that test case. Add the following static imports to your class.

  import static android.support.test.espresso.Espresso.onData;
  import static android.support.test.espresso.Espresso.onView;
  import static android.support.test.espresso.Espresso.pressBack;
  import static android.support.test.espresso.action.ViewActions.click;
  import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
  import static android.support.test.espresso.action.ViewActions.swipeUp;
  import static android.support.test.espresso.action.ViewActions.typeText;
  import static android.support.test.espresso.assertion.ViewAssertions.matches;
  import static android.support.test.espresso.matcher.ViewMatchers.withId;
  import static android.support.test.espresso.matcher.ViewMatchers.withText;
  import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
  import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
  import static android.support.test.espresso.action.ViewActions.click;
  import static android.support.test.espresso.action.ViewActions.scrollTo;
  import static android.support.test.espresso.action.ViewActions.doubleClick;
  import static org.hamcrest.Matchers.allOf;
  import static org.hamcrest.Matchers.containsString;
  import static org.hamcrest.Matchers.instanceOf;
  import static org.hamcrest.Matchers.is;
  import static org.hamcrest.Matchers.not;
  import static org.hamcrest.Matchers.anything;
  import static org.hamcrest.Matchers.equalTo;
  import static org.hamcrest.Matchers.hasEntry;

Android Studio can’t always derive what methods from what classes you’re trying to use. Plus, prefixing everything with Espresso.* is too much of a hassle. Thus, static imports. They can be trimmed as needed.

In newer versions of Android Studio, you can hover over unknown method calls and press Alt+Enter to bring up the suggestions menu. Select “Static import…”. This will add the static imports for you.

So, let’s look at our first bit of Espresso code.

  @Test
  public void fabShowsSnackBar() {
      // click on FAB
      onView( withId(R.id.fab) ).perform(click());

      // check that the snack bar shows up with the expected message
      onView( withText( R.string.text_snackbar ) ).check( matches( isDisplayed() ) );
  }

If you think it reads like a sentence, that’s exactly the intention. Hamcrest matchers and the Espresso library are both designed to be human-readable.

Though, knowing how to read isn’t the only requisite. There’s a bit of context to the pattern used when Espresso testing.

A Little More Context

So, there’s three major components to Espresso testing:

  • Espresso Methods
  • Matchers
  • Actions
  • Assertions

If you look at the code above, you’ll see that OnView, perform, and check are Espresso methods. withText and withId are both Matchers. click is an Action. matches is an assertion that takes in isDisplayed, another matcher.

Getting a Little Touchy

What if you don’t want to target a specific view? Like you just want to swipe stuff? You can do that, too.

In fact, the Android Instrumentation library can do that for you, but if you want to use Espresso for it, you’ll need to target a view.

So, make sure you have an ID on the view you want to swipe.

  @Test
  public void swipesDownFine() {
      // swipe the on the screen, can swipe: up, down, left, right
      onView( withId(R.id.coordinator_layout) ).perform(swipeDown());
  }

Inserting Text with Espresso

Alright, cool, you can swipe stuff and click stuff. Wow.

Now, what if you wanted to type text? Espresso’s got you covered.

  @Test
  public void typeIntoEditText() {
      String textToType = "Talk is cheap, show me the code.";

      // Type in some text into the field
      onView( withId(R.id.input_field) ).perform( typeText( textToType ) );

      // Check that the text is exactly what we typed
      onView( withId(R.id.input_field) ).check(matches(withText(textToType)));
  }

System Actions

All of the tests so far were dependent on the layouts we’re working with. What if we wanted to do something like rotate the phone? Or just go the Recents view?

While Espresso doesn’t have out-of-the-box support for anything besides .pressBack(), you can incorporate other testing libraries to cover for it.

Specifically, I’m referring to UiAutomator, a testing library for writing black-box UI tests. It provides methods for pressing various ActionBar buttons (e.g. Back, Home, Recents).

Note

With Espresso, people note that you can match the ActionBar buttons using their contentDescriptions. However, this is language specific, so if you’re testing an international app, your tests will fail.

On top of that, you would also have to use something like the uiautomatorviewer to see the View properties.

Testing a RecyclerView

Before we continue, I want to mention that Espresso doesn’t extensively support all of the new View classes. Specifically, I’m referring to RecyclerView. This is where the espresso-contrib dependency comes in- it’s an extension of the core library and comes with the RecyclerViewActions utility class.

Let’s write a test that clicks on an item in the list of the names.

  @Test
  public void clickOnItemInListOfNames() {
      // get recycler view from the activity
      RecyclerView mNamesList = (RecyclerView) mActivity.findViewById(R.id.recycler_view);

      if (mNamesList != null) {
          // get a random index based on the recycler view's item count
          int numItems = mNamesList.getAdapter().getItemCount();
          int randomIndex = new Random().nextInt(numItems);

          // click on the item at the random index
          ViewAction clickOnRandomItem = RecyclerViewActions.actionOnItemAtPosition(randomIndex, click());
          onView(withId(R.id.recycler_view)).perform(clickOnRandomItem);

          // make sure the name is populated in the EditText
          EditText item = (EditText) mActivity.findViewById(R.id.input_field);
          String namePicked = ((SimpleAdapter) mNamesList.getAdapter()).getItem(randomIndex);
          onView( withId(R.id.input_field) ).check( matches(withText(namePicked)) );
      }
  }

Take note of how un-Espresso the code looks. There must be a better solution, right? There is.

When you get deeper into Espresso, you’ll learn about more advanced features like custom Matcher classes. These utilize the same syntax and allow you to customize how Views are matched and validated against.

Next Steps

At this point, you should be ready to start running with Espresso. Of course, I didn’t cover everything and I strongly encourage going through the documentation and learning of the nifty little functions I didn’t mention.

Happy testing and Godspeed. ;P

you can do it

References

android testing ui tutorial