Espresso is a handy native Android tool for UI testing in Android. There is a lot of things you can test. Even though community support is pretty nice, it is sometimes time-consuming to find examples on testing your specific user cases. Therefore, I wrote this article to help you get an overview on some of the methods that Espresso supports. Android Developer documentation provides nice articles about the tests with some examples (more info here: https://developer.android.com/training/testing/ui-testing/espresso-testing.html). The picture below comes from developer.android.com and shows the methods supported by Espresso (click to zoom).
Create project and add gradle dependencies
Create a new project with empty activity. In order to access espresso features, you need to add its dependencies. You do it by opening your app’s build.gradle file and adding following lines:
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.2') { exclude module: 'espresso-core' exclude module: 'support-v4' exclude module: 'recyclerview-v7' exclude module: 'appcompat-v7' exclude module: 'support-annotations' exclude module: 'design' } androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2') { exclude module: 'rules' exclude module: 'javax.annotation-api' exclude module: 'support-annotations' } androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2.2') { exclude module: 'rules' exclude module: 'javax.annotation-api' exclude module: 'support-annotations' } androidTestCompile 'com.android.support.test:runner:0.5' androidTestCompile 'com.android.support.test:rules:0.5' // add this for intent mocking support androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2' // add this for webview testing support androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2' androidTestCompile 'com.android.support:support-annotations:25.3.1' testCompile 'junit:junit:4.12'
This configuration also prevents the gradle build error (stacktrace below) which gives a hard time to a lot of Espresso begginers.
Warning:Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (...) and test app (...) differ.
Initialize test class
Before you can write test, you have to create a class under app/src/androidTest/java/com/example/myapp. Your class path could be different, so replace the last 3 locations from the URI accordingly. In your Android Studio, the UI test folder comes with name androidTest in brackets, as shown on the picture below.
Create a new Java class with your desired name (in this tutorial it will be MyTest) and initialize it by inserting this code:
import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class MyTest { @Rule public IntentsTestRule mActivityRule = new IntentsTestRule<>(MainActivity.class); }
MainActivity.class refers to a class which will use your tests to run with. If you want to test UI elements of another class, refactor the parameter of the IntentsTestRule.
Probably the nicest thing about Espresso is that you do not need to inflate XML layouts. IntentsTestRule gives the testing class an idea about what activity will be tested and the specific activity has its own XML layout(s). Espresso will work with these layouts without any need to point them out.
Espresso RecyclerView with scrollToPosition()
This test method checks if – when scrolled to the last position in my RecyclerView, the text “This is the sixth row” is visible on screen (in my case the text is there, so the test passes).
@Test public void scrollRecyclerView() { // scrollTo() is not supported with RecyclerView, but elements like ListView. // Method scrollToPosition() from espresso-contrib library works the same way. // True test - if scrolled down, text is visible onView(withId(R.id.recycler_view)).perform(scrollToPosition(5)).check(matches(hasDescendant(withText("This is the sixth row")))); // False test - if scrolled at position 0 (no scroll), text not visible // onView(withId(R.id.recycler_view)).perform(scrollToPosition(0)).check(matches(hasDescendant(withText("This is the sixth row")))); }
Espresso New Intent with categories, flags and extras
When you want to check if new intent was started and parameters were passed along to this intent, you can use the test method below as a guideline. Let’s say we have a button like this one:
Button btn_intent = (Button) findViewById(R.id.btn_intent); btn_intent.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent n = new Intent(MainActivity.this,OtherActivity.class); n.addCategory(Intent.CATEGORY_INFO); n.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); n.putExtra("Activated", true); startActivity(n); } });
You can test this by writing a test method such as this one:
@Test public void ensureIntentStarted() throws InterruptedException { onView(withId(R.id.btn_intent)).perform(click()); intended( allOf( hasComponent(OtherActivity.class.getName()), hasCategories(hasItem(equalTo(Intent.CATEGORY_INFO))), hasFlag(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION), hasExtra("Activated",true) ) ); }
If you’d like to make a false test, change hasExtra(“Activated”, true) to hasExtra(“Activated”, false).
Espresso Element to right of other element
@Test public void ensureRightOf() { onView(ViewMatchers.withId(R.id.rightelement)).check(isRightOf(withId(R.id.leftelement))); }
Espresso Element below other element
@Test public void ensureBelow() { onView(ViewMatchers.withId(R.id.bottomelement)).check(isBelow(withId(R.id.topelement))); }
Espresso Element displayed on screen
@Test public void isDisplayed() { // True test - showntextview visibility = visible onView(withId(R.id.showntextview)).check(matches(ViewMatchers.isDisplayed())); // False test - hiddentext not displayed (visibility = gone) //onView(withId(R.id.hiddentextview)).check(matches(ViewMatchers.isDisplayed())); }
Espresso Element isEnabled()
@Test public void isEnabled() { onView(withId(R.id.checkbox)).check(matches(ViewMatchers.isEnabled())); }
Espresso Element isChecked()
@Test public void isChecked() { // True test - checkbox checked manually with perform(click()) onView(withId(R.id.checkbox)).perform(click()).check(matches(ViewMatchers.isChecked())); // False test - checkbox not checked //onView(withId(R.id.checkbox)).check(matches(ViewMatchers.isChecked())); }
Espresso Element isFocusable()
@Test public void isFocusable() { // True test - edittext is always focusable by default onView(withId(R.id.edittext)).check(matches(ViewMatchers.isFocusable())); // False test - textview focusable set to false in XML (focusable = false, focusableInTouchMode = false) //onView(withId(R.id.textnotfocusable)).check(matches(ViewMatchers.isFocusable())); }
Description on how Espresso tests position assertions
In order to describe Espresso’s way of tests, let’s take method isRightOf() as an example. I bet, when you first time heard/read about this method, the first thing that came across your mind was RelativeLayout’s alignRightOf property. It’s not wrong to assume that these two would have something in common, but it’s not really like that. Espresso compares positioning by comparing their pixel-to-pixel positions, rather than XML relational positions. The first picture shows a sample application.
As you can see by looking at arrows on this design, this layout is a RelativeLayout. The checkbox is positioned toRightOf edittext and they are both positioned above button with label ‘New intent’. Now at this point it is true to say that checkbox-rightof-edittext is true, as well as checkbox-rightof-button. Why is the second statement true?
Espresso does not care about XML positioning. It checks if the condition is true for elements that are rendered on the screen. Picture below shows the left boundary for the checkbox.
You can see that neither edittext, nor button crosses the position of checkbox after rendering (displaying on screen). That’s what is important for Espresso to know and that’s why the second statement from above is true, too. If you’d like to see a false test, you can take recyclerView as an example.
The width of recyclerView is shown using the line with arrow ends. It’s position overlaps horizontal position of the checkbox, which can no longer be toRightOf recyclerView. None of the elements on screen can be toRightOf any other element which has a width of 100%, in this case recyclerView. Similar explanation goes for isBelow() and isAbove() assertions, except that you have to switch axes which are compared. With that being said, I hope you now understand the logic behind Espresso. Good luck with testing!