// Start the main activity of the application under test mActivity = getActivity(); // Get a handle to the Activity object's main UI widget, a Spinner mSpinner = (Spinner) mActivity.findViewById(com.android.example.spinner.R.id.Spinner); // Set the Spinner to a known position mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION); // Stop the activity - The onDestroy() method should save the state of the Spinner mActivity.finish(); // Re-start the Activity - the onResume() method should restore the state of the Spinner mActivity = getActivity(); // Get the Spinner's current position int currentPosition = mActivity.getSpinnerPosition(); // Assert that the current position is the same as the starting position assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition); Test Activity state saving and restoration
Hamcrest Library of matchers Ŷ Syntactic sugar Ŷ Assert assertThat(someString, is(equalTo(“Expected”))); actual value expectation on the value (Matcher)
Hamcrest Lots of useful matchers Ŷ Custom matchers Ŷ not a number public void testSquareRootOfMinusOneIsNotANumber() { assertThat(Math.sqrt(-1), is(notANumber())); }
Hamcrest custom matcher Hamcrest matcher /** * Hamcrest matcher: test if a double has the value NaN (not a number) */ public class IsNotANumber extends TypeSafeMatcher {
@Override public boolean matchesSafely(Double number) { return number.isNaN(); }
public void describeTo(Description description) { description.appendText("not a number"); }
@Factory public static Matcher notANumber() { return new IsNotANumber(); } }
Espresso test anatomy @RunWith(AndroidJUnit4.class) @LargeTest public class NotesScreenTest { @Rule public ActivityTestRule notesActivityTestRule = new ActivityTestRule<>(NotesActivity.class);
Espresso test anatomy @RunWith(AndroidJUnit4.class) @LargeTest public class NotesScreenTest { @Rule public ActivityTestRule notesActivityTestRule = new ActivityTestRule<>(NotesActivity.class);
Espresso: exercise UI @Test public void addNoteToNotesList() throws Exception {
// Click on the add note button onView(withId(R.id.fab_add_notes)).perform(click());
// Add note title and description onView(withId(R.id.add_note_title)).perform(typeText(“More”)); onView(withId(R.id.add_note_description)).perform(typeText(“Testing”));
// Save the note onView(withId(R.id.fab_add_notes)).perform(click());
// Verify note is displayed on screen onView(withItemText(“Testing”)).check(matches(isDisplayed())); }
Espresso: exercise UI @Test public void addNoteToNotesList() throws Exception {
// Click on the add note button onView(withId(R.id.fab_add_notes)).perform(click());
// Add note title and description onView(withId(R.id.add_note_title)).perform(typeText(“More”)); onView(withId(R.id.add_note_description)).perform(typeText(“Testing”));
// Save the note onView(withId(R.id.fab_add_notes)).perform(click());
// Verify note is displayed on screen onView(withItemText(“Testing”)).check(matches(isDisplayed())); }
Espresso: exercise UI @Test public void addNoteToNotesList() throws Exception {
// Click on the add note button onView(withId(R.id.fab_add_notes)).perform(click());
// Add note title and description onView(withId(R.id.add_note_title)).perform(typeText(“More”)); onView(withId(R.id.add_note_description)).perform(typeText(“Testing”));
// Save the note onView(withId(R.id.fab_add_notes)).perform(click());
// Verify note is displayed on screen onView(withItemText(“Testing”)).check(matches(isDisplayed())); }
private static final ViewInteraction USERNAME_EDIT = onView(withId(R.id.login_username)); private static final ViewInteraction PASSWORD_EDIT = onView(withId(R.id.login_password)); private static final ViewInteraction LOGIN_BUTTON = onView(withId(R.id.login_button)); public static final ViewInteraction LOGIN_VALIDATION_USERNAME_INVALID_FORMAT = onView(withId(R.id.login_validation_username)); public static final ViewInteraction LOGIN_VALIDATION_INVALID_PASSWORD = onView(withId(R.id.login_validation_password));
public void typeUsername(String username) { USERNAME_EDIT.perform(clearText(), typeText(username)); }
public void typePassword(String password) { PASSWORD_EDIT.perform(clearText(), typeText(password)); }
public void clickLoginButton() { LOGIN_BUTTON.perform(scrollTo(), click()); }
Intent validation @Test public void validateIntentSentToPackage() { // User action that results in an external "phone" activity being launched. user.clickOnView(system.getView(R.id.callButton)); // Using a canned RecordedIntentMatcher to validate that an intent resolving // to the "phone" activity has been sent. intended(toPackage("com.android.phone")); }
Intent stubbing (Mockito for Intents) @Test public void activityResult_IsHandledProperly() { // Build a result to return when a particular activity is launched. Intent resultData = new Intent(); String phoneNumber = "012-345-678"; resultData.putExtra("phone", phoneNumber); ActivityResult result = new ActivityResult(Activity.RESULT_OK, resultData); // Set up result stubbing when an intent sent to "contacts" is seen. intending(toPackage("com.android.contacts")).respondWith(result)); // User action that results in "contacts" activity being launched. // Launching activity expects phoneNumber to be returned and displays it on the screen. onView(withId(R.id.pickButton)).perform(click()); // Assert that data we set up above is shown. onView(withId(R.id.phoneNumber).check(matches(withText(phoneNumber))); }
Espresso synchronization public class IdleMonitor implements IdlingResource {
@Override public String getName() { return IdleMonitor.class.getSimpleName(); }
@Override public boolean isIdleNow() { // return true if resource is idle return false; }
@Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { // store a reference to the resourceCallback // notify resourceCallback when idle } } Espresso.registerIdlingResources(idlingResource);
Mocking the Retrofit interface Canned data /** A mock implementation of the {@link GitHub} API interface. */ static class MockGitHub implements GitHub { private final Map>> ownerRepoContributors;
public MockGitHub() { ownerRepoContributors = new LinkedHashMap>>();
Mocking the Retrofit interface Mock // Create a very simple REST adapter which points to the GitHub API endpoint. RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(API_URL) .build();
// Wrap our REST adapter to allow mock implementations and fake network delay. MockRestAdapter mockRestAdapter = MockRestAdapter.from(restAdapter);
// Instantiate a mock object so we can interact with it later. MockGitHub mockGitHub = new MockGitHub(); // Use the mock REST adapter and our mock object to create the API interface. GitHub gitHub = mockRestAdapter.create(GitHub.class, mockGitHub);
// Query for some contributors for a few repositories. printContributors(gitHub, "square", "retrofit"); printContributors(gitHub, "square", "picasso");
Suggested reading Test Driven Development: By Example Beck, Kent (978-0321146533) Continuous Integration: Improving Software Quality and Reducing Risk Duvall, Paul M. et al. (978-0321336385) Working Effectively with Legacy Code Feathers, Michael (978-0131177055)