2. About me
Édipo Souza
Édipo Souza has over 3 years experience in android development with
Degree in Sistemas e Mídias Digitais by Federal University of Ceará (UFC)
and Specialization in Desenvolvimento de Sistemas para Dispositivos Móveis
by Faculdade Sete de Setembro (FA7).
He has certifications Oracle Certified Associate Java SE 8 Programmer and
Programming in HTML5 with JavaScript and CSS3 Specialist.
3. ● What’s Espresso?
● Why use Espresso?
● Configuration
● Android Test Rules
● Main Components
● Espresso Cheat Sheet
● JUnit Annotations
● Extra Libraries
○ Espresso-Contrib
○ Espresso-Intents
○ UI Automator
● Development Tips
● Commun Situations and Solution
Schedule
4. ● “A simple API for writing reliable UI tests” - Google
● Backward Compatibility Api 8 (Froyo) or Higher
● Open Source - https://code.google.com/p/android-test-kit
● Google Product - Presented at Google Test Automation Conference 2013
● Android Tool - Added to Android Testing Support Library in 2015 with
Version 2.1 released on 2015/04/21
● Actual version 2.2
What’s Espresso?
5. ● “The core API is small, predictable, and easy to learn and yet remains
open for customization” - Google
● Easy - “An extensive set of action APIs to automate UI interactions.”
● Extensible - “Flexible APIs for view and adapter matching in target apps.”
● Fast - “UI thread synchronization to improve test reliability.”
● No WaitFor(), No Sleep()!
● JUnit4 Support.
Why use Espresso?
6. ● Add these lines in your biuld.gradle file
Configuration
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
packagingOptions {
exclude 'NOTICE.txt'
exclude 'LICENSE.txt'
}
8. ● Make use of JUnit4 @Rule annotation
● Extends ActivityInstrumentationTestCase2 or ServiceTestCase are
deprecated
● ActivityTestRule
The activity under the Rule will be launched again in each Test
@Rule
public ActivityTestRule<MyActivity> activityRule = new ActivityTestRule<>(MyActivity.class);
● ServiceTestRule
The service will start and shutdown in each Test using startService it method
@Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule();
Android Test Rules
9. OK, show me Code!
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
public class MyActivityTest {
@Rule
public ActivityTestRule<MyActivity> mActivityRule = new ActivityTestRule<>(MyActivity.class);
@Test
public void testClickOKButtonAnd() {
}
}
10. Main Components
Matcher - Provide a way to find a view instance on
view hierarchy that matches a given criteria.
ViewAction - Provide instructions
to perform an Action
on view object.
ViewAssertion - Allows to check if some
view object state matches
with given criteria. This
can make the Test fail.
11. ViewMatcher
Matcher Categories
● User Properties
Commun component attributes
● UI Properties
Commun UI component state
● Object
Group, Logic and Text helpers
● Hierarchy
Identifiers based on relationship
● Input
Type support
● Class
Identifiers based on object class
● Root
Window holding view object
12. Let’s see it on Code!
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
public class MyActivityTest {
@Rule
public ActivityTestRule<MyActivity> mActivityRule = new ActivityTestRule<>(MyActivity.class);
@Test
public void testClickOKButtonAnd() {
//Find a button with text 'play'
onView(withText(R.string.play))
}
}
14. Cool, but and the Code?
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
public class MyActivityTest {
@Rule
public ActivityTestRule<MyActivity> mActivityRule = new ActivityTestRule<>(MyActivity.class);
@Test
public void testClickOKButtonAnd() {
//Click on button with text 'play'
onView(withText(R.string.play)).perform(click())
}
}
16. OK, but I want see Code!
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
public class MyActivityTest {
@Rule
public ActivityTestRule<MyActivity> mActivityRule = new ActivityTestRule<>(MyActivity.class);
@Test
public void testClickOKButtonAnd() {
//Click on button with text 'play' and Check if text change to 'stop'
onView(withText(R.string.play)).perform(click()).check(matches(withText(R.string.stop)));
}
}
//Click on button with text 'play'. Check if view with id btPlayStop has text 'stop'
onView(withText(R.string.play)).perform(click());
onView(withId(R.id.btPlayStop)).check(matches(withText(R.string.stop)));
//Click on item with text 'Track 3’ in a list of strings.
onData(allOf(is(instanceOf(String.class)), is("Track 3"))).perform(click());
18. Junit Annotations
@BeforeClass
The method is executed
before all test methods
@Before
This method is executed
before each test method
@AfterClass
This method is executed
after all test methods
@After
This method is
executed after each test
method
@BeforeClass
public static void setUp() {
...
}
@Before
public static void setupForEachTest() {
...
}
@Test
public void test1() {
...
}
@Test
public void test2() {
…
}
@After
public static void undoSetupForEachTest() {
…
}
@AfterClass
public static void undoSetup() {
…
}
19. ● Espresso-Contrib
Contains Actions to RecyclerView and system components like Pickers
● Espresso-Intents
Allows to Check or Mock response of started intents
● Espresso-Idling-Resource
Enable to define new resources to Espresso wait before proceed
● UI Automator
Let you do BlackBox test actions on system and third party apps
Extra Libraries
21. RecyclerViewActions
Needed because recyclerView extends ViewGroup, not AdapterView, so no onData for
it.
Espresso-Contrib
Actions
○ actionOnHolderItem(Matcher<VH> viewHolderMatcher, ViewAction viewAction)
○ actionOnItem(Matcher<View> itemViewMatcher, ViewAction viewAction)
○ actionOnItemAtPosition(int position, ViewAction viewAction)
Scroll
○ scrollTo(Matcher<View> itemViewMatcher)
○ scrollToHolder(Matcher<VH> viewHolderMatcher)
○ scrollToPosition(int position)
22. PickerActions
Needed for interact with DatePicker and TimerPicker
Espresso-Contrib
TimePicker
○ setTime(int hours, int minutes)
DatePicker
○ setDate(int year, int monthOfYear, int dayOfMonth)
23. ❖ intended(intentMatcher)
Allows validation of Intents sent out
by the application under test.
Espresso-Intents
❖ intending(intentMatcher)
.respondWith(activityResult)
Enables stubbing the response of
Intents sent by startActivityForResult
“It’s like Mockito, but for android intents.”
Matcher Categories
● Intent
Commun intent parameters
● URI
URI attributes validations
● Component Name
Component class and package validations
● Bundle
Search intent based on Entry, Key or
Value
25. Cool, I want see it on Code!
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiSelector;
@RunWith(AndroidJUnit4.class)
public class MyActivityTest {
@Test
public void openMemoAndAddANewNote() throws Exception {
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
}
}
//Go to launcher home
uiDevice.pressHome();
//click apps button to open apps drawer
uiDevice.findObject(new UiSelector().description("Apps")).clickAndWaitForNewWindow();
//open calculator
uiDevice.findObject(new UiSelector().text("Calculator")).clickAndWaitForNewWindow();
//Calculate 3*9
uiDevice.findObject(new UiSelector().description("3")).click();
uiDevice.findObject(new UiSelector().description("Multiply")).click();
uiDevice.findObject(new UiSelector().description("9")).click();
uiDevice.findObject(new UiSelector().description("equal")).click();
//create a selector for result display text based on its resourceId
UiSelector resultSelector = new UiSelector().resourceId("com.android.calculator2:id/display_Res_Text");
//check result
assertEquals("equal 27", uiDevice.findObject(resultSelector).getText());
26. “ UI Automator is well-suited for writing black box-style automated tests”
UI Automator Viewer
28. ● Get the application context
● Get the test application context
● Obtain the context of activity to be tested
● The withText() method can be used with a string resource id
Development Tips
InstrumentationRegistry.getTargetContext();
Test class
activityRule.getActivity().getApplicationContext();
Activity
InstrumentationRegistry.getContext();
@Rule
public ActivityTestRule<MyActivity> activityRule = new ActivityTestRule<>(MyActivity.class);
activityRule.getActivity().getContext();
withText(R.string.test)
29. ● When is expected that the test returns an exception, simply add the
expected exception to Test annotation:
● However for large testing is recommended to use use ExpectedException
Rule
Development Tips
@Test(expected = IndexOutOfBoundsException.class)
public void test1() {
}
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
List<Object> list = new ArrayList<Object>();
thrown.expect(IndexOutOfBoundsException.class);
thrown.expectMessage("Index: 0, Size: 0");
list.get(0); // execution will never get past this line
}
30. ● To interact with the items from Spinner, PopupMenu or similar use:
'.inRoot(isPlatformPopup())'
● Stay tuned when performs swipe in a pageView to find an item in list.
Perform a click in a tab for that the items can be found
Commun Situations/Solution
//Clicks in spinner
onView(withId(R.id.spinnerCountries)).perform(click());
//Select the country Brazil and clicks in it
onData(allOf(is(instanceOf(String.class)), is(“Brazil”))).inRoot(isPlatformPopup()).perform(click());
//Click TAB1
onView(withText(“TAB1”)).perform(click());
31. ● As RecyclerView extends ViewGroup should be used the onView
method to find items in list instead of onData
● If a RecyclerView item is not visible, unlike the ListView, a scroll must be
made to the item using RecyclerViewActions.scrollTo() before interacting
with the item.
Commun Situations/Solution
// Perform a click on first element in the RecyclerView
onView(withId(R.id.recyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));
Object rvTag = 0;
Matcher<View> viewMatcher = hasDescendant(withText(“Item 100”));
onView(withTagValue(is(rvTag))).perform(RecyclerViewActions.scrollTo(viewMatcher));
32. ● To check whether a toast was displayed
● To perform an action conditionally, we can override the method
withFailureHandler, then if the check fails, instead of finishing the test,
the method handle is called
Commun Situations/Solution
//Get the decorView
View activityDecorView = mActivityRule.getActivity().getWindow().getDecorView();
onView(withText(TOAST_TEXT)).inRoot(withDecorView(not(activityDecorView))).check(isDisplayed());
onView(withText(R.string.dialog_config_app)).withFailureHandler(new FailureHandler() {
@Override
public void handle(Throwable throwable, Matcher<View> matcher) {
onView(withText(android.R.string.ok)).perform(click());
}
}).check(doesNotExist());