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

Android Testing Support Library - The Nitty Gritty

Avatar for Zan Markan Zan Markan
November 24, 2016

Android Testing Support Library - The Nitty Gritty

Talk I've given at Tokyo Android Meetup.

Avatar for Zan Markan

Zan Markan

November 24, 2016
Tweet

More Decks by Zan Markan

Other Decks in Programming

Transcript

  1. Testing 101 • on JVM vs on device • unit

    / integration / functional / end to end • Robolectric, Calabash, Instrumentation, Robotium, Appium © Zan Markan 2016 - @zmarkan
  2. "Support" • Android framework vs Support libraries • Trend to

    unbundle • support-v4, appcompat-v7, recyclerview, ... © Zan Markan 2016 - @zmarkan
  3. More jUnit3 goodness • overriding setUp() and tearDown() • testPrefixedMethods()

    & test_prefixedMethods() • Ignorance Inheritance is bliss © Zan Markan 2016 - @zmarkan
  4. Welcome to the present • jUnit4 syntax • No more

    extending • @Test, @Before, @After, @AfterClass,... • ActivityTestRule, InstrumentationRegistry © Zan Markan 2016 - @zmarkan
  5. What else is in there? • Espresso • More Espresso

    (there's a lot to it) • UIAutomator • Test Runner • Test Rules • ... © Zan Markan 2016 - @zmarkan
  6. But First... The setup (note: AS does that on it's

    own if you create a new project - these instructions will mostly apply for legacy projects) © Zan Markan 2016 - @zmarkan
  7. Gradle • Set your test runner to be AndroidJUnitRunner •

    Add dependencies • Voila! © Zan Markan 2016 - @zmarkan
  8. Error:Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.2.1) and

    test app (23.1.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details. © Zan Markan 2016 - @zmarkan
  9. Gradle • Set your test runner to be AndroidJUnitRunner •

    Add dependencies • Voila! • Resolve dependencies © Zan Markan 2016 - @zmarkan
  10. Dependency resolutions • App and Test app depend on different

    lib versions • Run ./gradlew :app:dependencies • ! in the compile and androidTestCompile tasks © Zan Markan 2016 - @zmarkan
  11. Resolve with • Force dependency versions in the test APK

    • exclude dependency (everywhere applicable) • use Resolution strategy © Zan Markan 2016 - @zmarkan
  12. Force versions in test APK // Resolve conflicts between main

    and test APK: androidTestCompile "com.android.support:support-annotations:$rootProject.supportLibraryVersion" androidTestCompile "com.android.support:support-v4:$rootProject.supportLibraryVersion" Source: github.com/googlesamples/android- architecture/blob/todo-mvp/todoapp/app/build.gradle © Zan Markan 2016 - @zmarkan
  13. Gradle • Set your test runner to be AndroidJUnitRunner •

    Add dependencies • Resolve dependencies • Voila! © Zan Markan 2016 - @zmarkan
  14. Espresso - components • View interactions & assertions • Hamcrest

    syntax • No (unnecessary) waits © Zan Markan 2016 - @zmarkan
  15. Poking the screen onView(withId(R.id.button)).perform(click()); • allOf, anyOf, ... • withParent,

    withText... • isDisplayed, isDialog... © Zan Markan 2016 - @zmarkan
  16. Espresso - contrib • RecyclerView • Drawers • Pickers •

    Accessibility © Zan Markan 2016 - @zmarkan
  17. Espresso++ • Custom matchers • Custom ViewActions • Idling resources

    • Page objects • Intent mocking © Zan Markan 2016 - @zmarkan
  18. Custom matchers • Find a view in the hierarchy •

    Good for custom views & components • Override: • describeTo • matchesSafely © Zan Markan 2016 - @zmarkan
  19. Custom matchers interface MyView{ boolean hasCustomProperty(); } static BoundedMatcher withCustomPropery()

    { return new BoundedMatcher<Object, MyView>(MyView.class){ @Override public void describeTo(Description description) { description.appendText("Custom property is enabled"); } @Override protected boolean matchesSafely(MyView item) { return item.hasCustomProperty(); } }; } © Zan Markan 2016 - @zmarkan
  20. Custom ViewActions • Same story as matchers, just a bit

    more extensive • example allows us to scroll in NestedScrollView • github.com/zmarkan/Android-Espresso- ScrollableScroll © Zan Markan 2016 - @zmarkan
  21. Custom ViewActions - API public class ScrollableUtils { public static

    ViewAction scrollableScrollTo() { return actionWithAssertions(new ScrollableScrollToAction()); } } © Zan Markan 2016 - @zmarkan
  22. Implementation... public class ScrollableScrollToAction implements ViewAction{ private static final String

    TAG = com.zmarkan.nestedscroll.action.ScrollableScrollToAction.class.getSimpleName(); @SuppressWarnings("unchecked") @Override public Matcher<View> getConstraints() { return allOf(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE), isDescendantOfA(anyOf( isAssignableFromClassOrInterface(ScrollingView.class)))); } @Override public void perform(UiController uiController, View view) { if (isDisplayingAtLeast(90).matches(view)) { Log.i(TAG, "View is already displayed. Returning."); return; } Rect rect = new Rect(); view.getDrawingRect(rect); if (!view.requestRectangleOnScreen(rect, true /* immediate */)) { Log.w(TAG, "Scrolling to view was requested, but none of the parents scrolled."); } uiController.loopMainThreadUntilIdle(); if (!isDisplayingAtLeast(90).matches(view)) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException( "Scrolling to view was attempted, but the view is not displayed")) .build(); } } @Override public String getDescription() { return "scroll to"; } } © Zan Markan 2016 - @zmarkan
  23. Implementation... @Override public void perform(UiController uiController, View view) { if

    (isDisplayingAtLeast(90).matches(view)) { Log.i(TAG, "View is already displayed. Returning."); return; } Rect rect = new Rect(); view.getDrawingRect(rect); if (!view.requestRectangleOnScreen(rect, true /* immediate */)) { Log.w(TAG, "Scrolling to view was requested, but none of the parents scrolled."); } uiController.loopMainThreadUntilIdle(); if (!isDisplayingAtLeast(90).matches(view)) { throw new PerformException.Builder() .withActionDescription(this.getDescription()) .withViewDescription(HumanReadables.describe(view)) .withCause(new RuntimeException( "Scrolling to view was attempted, but the view is not displayed")) .build(); } } © Zan Markan 2016 - @zmarkan
  24. Don't panic. • Take something that works • ...like scrollTo()

    in regular Espresso for ScrollView • modify it • profit © Zan Markan 2016 - @zmarkan
  25. Custom IdlingResource • A better way to wait • Use

    when you have background stuff going on • Override: • getName • isIdleNow • registerIdleTransitionCallback © Zan Markan 2016 - @zmarkan
  26. Custom IdlingResource public class CustomIdlingResource implements IdlingResource{ private ResourceCallback resourceCallback;

    private EventBus bus; private boolean loadingCompleted = false; public CustomIdlingResource(EventBus bus){ bus.register(this); } public void onEvent(LoadingCompletedEvent event){ loadingCompleted = true; isIdleNow(); } } © Zan Markan 2016 - @zmarkan
  27. Custom IdlingResource @Override public String getName() { return CustomIdlingResource.class.getName(); }

    @Override public void registerIdleTransitionCallback( ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; } @Override public boolean isIdleNow() { boolean idle = loadingCompleted; if (idle && resourceCallback != null) { resourceCallback.onTransitionToIdle(); bus.unregister(this); } return idle; } } © Zan Markan 2016 - @zmarkan
  28. Page Object Pattern • Build your own DSL • Every

    screen is a Page • Page-specific actions and verifications • (as seen in Calabash, etc...) © Zan Markan 2016 - @zmarkan
  29. Page Class public class MainViewTestUtils { public void enterUserName(String text){

    /* espresso goes here */ } public void enterPassword(String text){ /* ... */ } public void pressContinue(){ /* ... */ } public void assertErrorShown(boolean shown){ /* ... */ } } © Zan Markan 2016 - @zmarkan
  30. Page Class in test @Test public void errorShownWhenPasswordIncorrect(){ MainViewTestUtils view

    = new MainViewTestUtils(); view.enterUsername(username); view.enterPassword(incorrectPassword); view.pressContinue(); view.assertErrorShown(true); } © Zan Markan 2016 - @zmarkan
  31. UI Automator • Interact with any installed app • Use

    to create full end-to-end tests • Can coexist with Espresso in the same app • Use uiautomatorviewer command to find items in the hierarchy © Zan Markan 2016 - @zmarkan
  32. Test Runner • Spins up tests... • and runs them

    • Customise to prepare mocks • Easier run/debug via command line © Zan Markan 2016 - @zmarkan
  33. Extending the Runner • Use custom Application class • Provide

    mocked dependencies • Specify this new runner in build.gradle • Kotlin Test Runner © Zan Markan 2016 - @zmarkan
  34. Extending the Runner public class CustomRunner extends AndroidJUnitRunner{ @Override public

    Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return Instrumentation.newApplication(TestApplication.class, context); } } © Zan Markan 2016 - @zmarkan
  35. Running on the Command line • Run module/package/class/method • Run

    Large/Medium/Small test only • Shard to run in parallel • Debug without reinstalling © Zan Markan 2016 - @zmarkan
  36. Simple API • send commands to runner via ADB (adb

    shell [commands]) • am instrument -w -e class your.full.classname#methodName your.test.package.name/ your.test.Runner.class • d.android.com/tools/testing/testing_otheride.html © Zan Markan 2016 - @zmarkan
  37. Test Rules • Set starting activity / Service • Replace

    ActivityInstrumentationTestCase2 • (in most cases) • Add / Extend to create more components © Zan Markan 2016 - @zmarkan
  38. Test Rules - examples • MockWebServerRule - sets up MockWebServer

    when required • Source: github.com/artem-zinnatullin/ qualitymatters © Zan Markan 2016 - @zmarkan
  39. Firebase test lab • Simple setup • CI support (via

    gcloud) • Support for VMs • firebase.google.com/docs/test-lab • Robo test for automated testing © Zan Markan 2016 - @zmarkan
  40. Espresso Test Recorder • Since AS 2.2 preview 3 •

    Generates test code after clicking on screen • (Not necessarily nice code) • tools.android.com/tech-docs/test-recorder © Zan Markan 2016 - @zmarkan
  41. Above & Beyond? • Espresso Web for WebViews • JankTestHelper

    • Stuff is being added. © Zan Markan 2016 - @zmarkan
  42. Pusher /ˈpʊʃ ər/ noun 1. Platform of APIs for building

    highly connected apps 2. Hiring in London ! © Zan Markan 2016 - @zmarkan
  43. fin • ! www.spacecowboyrocketcompany.com • " @zmarkan • # zan

    at markan dot me • $ androidchat.co (zmarkan) • %& @zmarkan © Zan Markan 2016 - @zmarkan
  44. • google.github.io/android-testing-support-library/ contribute • Quality Matters - Artem Zinnatulin •

    d.android.com/tools/testing-support-library • github.com/googlesamples/android-testing • chiuki.github.io/advanced-android-espresso © Zan Markan 2016 - @zmarkan
  45. • Espresso: Brian Legate (Flickr) • USS Enterprise: Randomwallpapers.net •

    Road Runner: BrownZelip (Flickr) • Back to the future: Youtube © Zan Markan 2016 - @zmarkan