Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Testing at Mobel (December 9, 2015)

Android Testing at Mobel (December 9, 2015)

Slides for the 'Testing on mobile' session (Mobel user group : http://www.meetup.com/mo-bel/events/226864160/)

Filip Maelbrancke

December 09, 2015
Tweet

More Decks by Filip Maelbrancke

Other Decks in Programming

Transcript

  1. // 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
  2. Google Android Testing Codelab Best (introductory) Android testing example available

    at the moment (imho) https://www.code-labs.io/codelabs/android-testing/
  3. Espresso API onView(Matcher<View>) onData(Matcher<Object>) Espresso withId() withText() ViewMatchers perform() check()

    ViewInteraction / DataInteraction click() enterText() scrollTo() ViewActions matches(Matcher<View>) doesNotExist() ViewAssertions find view to operate on composed of © Stephan Linzner (Google)
  4. Espresso test anatomy @RunWith(AndroidJUnit4.class) @LargeTest public class NotesScreenTest { @Rule

    public ActivityTestRule<NotesActivity> notesActivityTestRule = new ActivityTestRule<>(NotesActivity.class);
  5. Espresso test anatomy @RunWith(AndroidJUnit4.class) @LargeTest public class NotesScreenTest { @Rule

    public ActivityTestRule<NotesActivity> notesActivityTestRule = new ActivityTestRule<>(NotesActivity.class);
  6. 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()));
 }
  7. 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()));
 }
  8. 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()));
 }
  9. Screen Object Pattern public class LoginScreen {
 
 private static

    final ViewInteraction USERNAME = onView(withId(R.id.username));
 private static final ViewInteraction PASSWORD = onView(withId(R.id.password));
 private static final ViewInteraction LOGIN = onView(withId(R.id.loginbutton));
 
 public void typeUsername(String username) {
 USERNAME.perform(clearText(), typeText(username));
 }
 
 public void typePassword(String password) {
 PASSWORD.perform(clearText(), typeText(password));
 }
 
 public void clickLoginButton() {
 LOGIN.perform(click());
 }
 
 }
  10. 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")); }
  11. Intent stubbing @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))); }
  12. 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);
  13. OKHTTP MockWebServer public void testServerErrorShouldShowEmptyState() { MockServiceModule mockServiceModule = new

    MockServiceModule(); DaggerHelper.initWithTestModules(mockServiceModule); // Setup the MockWebServer MockResponse mockResponse = new MockResponse(); mockResponse.setResponseCode(500); mockServiceModule.getMockWebServer().enqueue(mockResponse); // mockwebserver + restadapter are setup, now init activity getActivity(); onView(withId(R.id.list_empty_image)).check( matches(isDisplayed())); }
  14. Custom Test Rules @Rule
 public EnableTestTracing enableTestTracing;
 
 @Rule
 public

    EnablePostTestDumpsys enablePostTestDumpsys;
 
 @Rule
 public EnableLogcatDump enableLogcatDump;
 
 @Rule
 public EnableNetStatsDump enableNetStatsDump;
  15. Android Unmock Gradle Plugin unMock { // URI to download

    the android-all.jar from downloadFrom 'https://oss.sonatype.org/content/groups/public/org/robolectric/android-all/4.3_r2-robolectric-0/android- all-4.3_r2-robolectric-0.jar' keep "android.widget.BaseAdapter" keep "android.widget.ArrayAdapter" keep "android.os.Bundle" keepStartingWith "android.text.TextUtils" keepStartingWith "android.util." keepStartingWith "android.text." keepStartingWith "com.android.internal.R" keepStartingWith "com.android.internal.util." keep "android.net.Uri" }
  16. AssertJ Android for checking assertions to read / write http://square.github.io/assertj-android/

    Syntactic sugar Ŷ Extension of the AssertJ library Ŷ Makes tests easier Ŷ Fluent syntax Ŷ
  17. AssertJ Android Regular JUnit assertEquals(View.VISIBLE, layout.getVisibility()); assertEquals(VERTICAL, layout.getOrientation()); assertEquals(4, layout.getChildCount());

    assertEquals(SHOW_DIVIDERS_MIDDLE, layout.getShowDividers()); AssertJ Android assertThat(layout).isVisible() .isVertical() .hasChildCount(4) .hasShowDividers(SHOW_DIVIDERS_MIDDLE);
  18. Awaitility @Test
 public void test_mediaPlayer_liveStream() {
 
 MediaControllerCompat mediaController =

    new MediaControllerCompat(getContext(), audioPlayerService.getMediaSessionToken());
 String uri = "http://stream.be/service/mp3:web/program_ondemand_128.mp3/playlist.m3u8";
 Bundle extras = new Bundle();
 extras.putBoolean(AudioPlayerService.EXTRA_CONTINUE_ON_RESUME, false);
 mediaController.getTransportControls().playFromMediaId(uri, extras);
 await().atMost(10, TimeUnit.SECONDS).until(musicIsActive());
 
 } Test asynchronous system Express expectations in easy to read manner
  19. Creating software = complex Continuous integration Ensure quality Automate high-quality,

    robust and reliable apps tedious / error-prone activities
  20. Continuous integration 1 2 3 4 CODE & COMMIT BUILD

    & CHECK CI PICKUP REPORT RESULTS
  21. Build pipeline Checkout / compile Unit tests Test coverage Code

    analysis Create deployable artifact Deploy for automatic QA test Trigger automated QA stage
  22. Auto publish Delivery Promote app to production Automate Google Play

    alpha / beta iTunes Connect without additional tools
  23. Resources AT&T Application Resource Optimizer (mentioned by Friedger Müffke &

    Jo Somers) https://developer.att.com/application-resource-optimizer Wiremock http://wiremock.org/ Android Unmock Gradle Plugin https://github.com/bjoernQ/unmock-plugin AssertJ Android https://github.com/square/assertj-android Awaitility https://github.com/jayway/awaitility Spoon https://square.github.io/spoon/
  24. 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)