Espresso and the Navigation Drawer

Espresso has a full feature set when it comes to testing your Android UI. Almost everything you might need can be easily achieved using the provided API. Sometimes, you can still run into things that Espresso doesn’t explicitly cover, requiring deeper investigation, and more writing. Often this turns into much more code, requiring you to write custom matchers, and actions.

I ran into one of these cases the other day while testing a navigation drawer. I wanted to close the navigation drawer using an outside tap instead of the provided close drawer action. This wasn’t as straightforward as I hoped.

Because there is no “click outside this view” function, I first tried writing my test to perform a click on an element under the navigation drawer, still visible to the user while the drawer is open. It’s grayed out, but you can still see it, right?

@Test
fun clickingOutsideClosesNavigationDrawer() {
activity.launchActivity(null)
onView(withContentDescription(R.string.label_drawer_open))
.perform(click())
  // Click on button visible when drawer open
onView(withId(R.id.button))
.perform(click())
  onView(withId(R.id.navigation_view))
.check(matches(not(isDisplayed())))
}

This did not work. Even though it may be visible under that shadow, it fails because it is not “displayed.” Time to try something else.

We’re going to have to pick coordinates to tap on directly. We can pick these by looking at the boundaries of the navigation drawer view, and adding a couple pixels to move them just outside the view. Then we can use clickOnCoordinates to click on this location

Note: these specific calculations are for a start gravity navigation drawer.

We create a helper function to accomplish this. We need both the drawer view, and the ID of the layout.

private fun clickOutsideDrawer(
parentDrawerLayout: View, @LayoutRes drawId: Int) {
onView(withId(drawId))
.perform(
CoordinatesClickViewAction.clickOnCoordinates(
parentDrawerLayout.x + parentDrawerLayout.width + 10,
parentDrawerLayout.height / 2f))
}

Then, we can use it in our test to click outside the navigation drawer!

@Test
fun clickingOutsideClosesNavigationDrawer() {
activity.launchActivity(null)
onView(withContentDescription(R.string.label_drawer_open))
.perform(click())
  val parentDrawerLayout = 
activity.activity.findViewById<View>(R.id.navigation_view)
clickOutsideDrawer(parentDrawerLayout, R.id.drawer_main)
  onView(withId(R.id.navigation_view))
.check(matches(not(isDisplayed())))
}

Happy coding!