android testing support library: the nitty gritty - zan markan - codemotion milan 2016

64
© Zan Markan 2016 - @zmarkan

Upload: codemotion

Post on 07-Jan-2017

79 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

© Zan Markan 2016 - @zmarkan

Page 2: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Android Testing Support Library

The Nitty Gritty

© Zan Markan 2016 - @zmarkan

Page 3: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Testing 101• on JVM vs on device• unit / integration / functional / end to end• Robolectric, Calabash, Instrumentation, Robotium,

Appium

© Zan Markan 2016 - @zmarkan

Page 4: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

"Support"• Android framework vs Support libraries• Trend to unbundle• support-v4, appcompat-v7, recyclerview, ...

© Zan Markan 2016 - @zmarkan

Page 5: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

"The support library is basically a mountain of hacks"— Chris Banes, Google

© Zan Markan 2016 - @zmarkan

Page 6: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Android Testing Support Library

© Zan Markan 2016 - @zmarkan

Page 7: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Good Old Times...• jUnit3 syntax• Remember

ActivityInstrumentationTestCase2<MainActivit>?

© Zan Markan 2016 - @zmarkan

Page 8: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

More jUnit3 goodness• overriding setUp() and tearDown()

• testPrefixedMethods() & test_prefixedMethods()

• Ignorance Inheritance is bliss

© Zan Markan 2016 - @zmarkan

Page 9: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Welcome to the present• jUnit4 syntax• No more extending

• @Test, @Before, @After, @AfterClass,...

• ActivityTestRule, InstrumentationRegistry

© Zan Markan 2016 - @zmarkan

Page 10: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

What else is in there?• Espresso• More Espresso (there's a lot to it)• UIAutomator• Test Runner• Test Rules• ...

© Zan Markan 2016 - @zmarkan

Page 11: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

What else is in there?

© Zan Markan 2016 - @zmarkan

Page 12: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 13: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Gradle• Set your test runner to be AndroidJUnitRunner• Add dependencies• Voila!

© Zan Markan 2016 - @zmarkan

Page 14: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 15: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Gradle• Set your test runner to be AndroidJUnitRunner• Add dependencies• Voila!• Resolve dependencies

© Zan Markan 2016 - @zmarkan

Page 16: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 17: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Resolve with• Force dependency versions in the test APK• exclude dependency (everywhere applicable)• use Resolution strategy

© Zan Markan 2016 - @zmarkan

Page 18: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 19: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Gradle• Set your test runner to be AndroidJUnitRunner• Add dependencies• Resolve dependencies• Voila!

© Zan Markan 2016 - @zmarkan

Page 20: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

© Zan Markan 2016 - @zmarkan

Page 21: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Espresso - components• View interactions & assertions• Hamcrest syntax• No (unnecessary) waits

© Zan Markan 2016 - @zmarkan

Page 22: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Espresso - API

Cheat sheet:google.github.io/android-testing-support-library/docs/espresso/cheatsheet

© Zan Markan 2016 - @zmarkan

Page 23: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Poking the screenonView(withId(R.id.button)).perform(click());

© Zan Markan 2016 - @zmarkan

Page 24: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Poking the screenonView(withId(R.id.button)).perform(click());

• allOf, anyOf, ...

• withParent, withText...

• isDisplayed, isDialog...

© Zan Markan 2016 - @zmarkan

Page 25: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Espresso - contrib• RecyclerView• Drawers• Pickers• Accessibility

© Zan Markan 2016 - @zmarkan

Page 26: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Espresso++• Custom matchers• Custom ViewActions• Idling resources• Page objects• Intent mocking

© Zan Markan 2016 - @zmarkan

Page 27: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Custom matchers• Find a view in the hierarchy• Good for custom views & components• Override:

• describeTo

• matchesSafely

© Zan Markan 2016 - @zmarkan

Page 28: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Custom matchersinterface 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

Page 29: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 30: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Custom ViewActions - APIpublic class ScrollableUtils {

public static ViewAction scrollableScrollTo() { return actionWithAssertions(new ScrollableScrollToAction()); }}

© Zan Markan 2016 - @zmarkan

Page 31: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 32: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Implementation...@Overridepublic Matcher<View> getConstraints() { return allOf(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE), isDescendantOfA(anyOf( isAssignableFromClassOrInterface(ScrollingView.class))));}

© Zan Markan 2016 - @zmarkan

Page 33: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Implementation...@Overridepublic 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

Page 34: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Don't panic.• Take something that works

• ...like scrollTo() in regular Espresso for ScrollView• modify it• profit

© Zan Markan 2016 - @zmarkan

Page 35: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Custom IdlingResource• A better way to wait• Use when you have background stuff going on• Override:

• getName

• isIdleNow

• registerIdleTransitionCallback

© Zan Markan 2016 - @zmarkan

Page 36: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Custom IdlingResourcepublic 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

Page 37: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 38: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 39: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Page Classpublic 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

Page 40: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Page Class in test@Testpublic void errorShownWhenPasswordIncorrect(){

MainViewTestUtils view = new MainViewTestUtils();

view.enterUsername(username); view.enterPassword(incorrectPassword); view.pressContinue(); view.assertErrorShown(true);}

© Zan Markan 2016 - @zmarkan

Page 41: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

To boldly go...

© Zan Markan 2016 - @zmarkan

Page 42: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 43: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

UI Automator viewer

© Zan Markan 2016 - @zmarkan

Page 44: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

UI Automator syntaxUiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation();

device.pressHome();

device.findObject(new UiSelector().descriptionContains("Google search")).clickAndWaitForNewWindow();

© Zan Markan 2016 - @zmarkan

Page 45: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Test Runner

© Zan Markan 2016 - @zmarkan

Page 46: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Test Runner• Spins up tests...• and runs them• Customise to prepare mocks• Easier run/debug via command line

© Zan Markan 2016 - @zmarkan

Page 47: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Extending the Runner• Use custom Application class• Provide mocked dependencies• Specify this new runner in build.gradle• Kotlin Test Runner

© Zan Markan 2016 - @zmarkan

Page 48: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Extending the Runnerpublic 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

Page 49: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 50: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 51: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Test Rules

© Zan Markan 2016 - @zmarkan

Page 52: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Test Rules• Set starting activity / Service

• Replace ActivityInstrumentationTestCase2• (in most cases)• Add / Extend to create more components

© Zan Markan 2016 - @zmarkan

Page 54: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

© Zan Markan 2016 - @zmarkan

Page 55: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 56: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

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

Page 57: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Above & Beyond?• Espresso Web for WebViews• JankTestHelper• Stuff is being added.

© Zan Markan 2016 - @zmarkan

Page 58: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Best of all?

© Zan Markan 2016 - @zmarkan

Page 59: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

It's all open source!

© Zan Markan 2016 - @zmarkan

Page 60: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

Pusher/ˈpʊʃ ər/

noun1. Platform of APIs for building highly connected apps2. Hiring in London !

© Zan Markan 2016 - @zmarkan

Page 61: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

fin• ! www.spacecowboyrocketcompany.com

• " @zmarkan

• # zan at markan dot me

• $ androidchat.co (zmarkan)

• %& @zmarkan

© Zan Markan 2016 - @zmarkan

Page 62: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

© Zan Markan 2016 - @zmarkan

Page 63: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

• 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

Page 64: Android Testing Support Library: The Nitty Gritty - Zan Markan - Codemotion Milan 2016

• Espresso: Brian Legate (Flickr)• Hyperdrive: Youtube• Road Runner: BrownZelip (Flickr)• Back to the future: Youtube• Titanic / unit tests passing: Twitter

© Zan Markan 2016 - @zmarkan