Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Android Testing Support Library: the nitty gritty
Search
Zan Markan
November 26, 2016
Programming
85
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Android Testing Support Library: the nitty gritty
Talk delivered at Codemotion Milan 2016
Zan Markan
November 26, 2016
More Decks by Zan Markan
See All by Zan Markan
High-Performing Engineering Teams and the Holy Grail
zmarkan
0
130
A Practical Introduction to CI/CD
zmarkan
0
59
The Need for Speed - Practical Tips for Optimising your CI/CD Pipeline
zmarkan
0
95
Chat app with React, Auth0, and Pusher Chatkit
zmarkan
0
410
State of Kotlin - Droidcon NYC
zmarkan
1
120
State of Kotlin - Droidcon Berlin 2018
zmarkan
1
190
Building DSLs in Kotlin for Fun and Profit
zmarkan
2
610
Push Notifications That Don't Suck
zmarkan
3
470
The State of Kotlin
zmarkan
0
220
Other Decks in Programming
See All in Programming
手動確認はもう限界 〜XCUITestでCustom URL Schemeの遷移を起動種別ごとに自動テストする〜 / Testing Custom URL Schemes with XCUITest
otouto
0
310
The Rails Doctrine Decade
koic
2
170
Security issues being discussed on Web Platforms
petamoriken
0
1k
更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話
ken_tunc
0
310
XHTMLが残したもの
yosuke_furukawa
PRO
2
830
Heart of Swift Concurrency
koher
0
930
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
4.8k
新卒PdEのリアル
ryu1013
1
510
速く作れる。その次は、速く確かめられる開発へ 〜AIネイティブ開発を支える、Shift Down〜 / Can build fast. Next, moving to development where we can verify fast.
rkaga
5
3.6k
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
290
【高い買い物LT会】初任給で話題の国産フィジカルAIを買った話
akagami
PRO
0
160
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
230
Featured
See All Featured
HDC tutorial
michielstock
2
880
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
My Coaching Mixtape
mlcsv
0
320
Prompt Engineering for Job Search
mfonobong
0
450
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.1k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
460
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
Claude Code のすすめ
schroneko
67
230k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
65k
Designing Experiences People Love
moore
143
24k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.6k
Transcript
© Zan Markan 2016 - @zmarkan
Android Testing Support Library The Nitty Gritty © Zan Markan
2016 - @zmarkan
Testing 101 • on JVM vs on device • unit
/ integration / functional / end to end • Robolectric, Calabash, Instrumentation, Robotium, Appium © Zan Markan 2016 - @zmarkan
"Support" • Android framework vs Support libraries • Trend to
unbundle • support-v4, appcompat-v7, recyclerview, ... © Zan Markan 2016 - @zmarkan
"The support library is basically a mountain of hacks" —
Chris Banes, Google © Zan Markan 2016 - @zmarkan
Android Testing Support Library © Zan Markan 2016 - @zmarkan
Good Old Times... • jUnit3 syntax • Remember ActivityInstrumentationTestCase2<MainActi vit>?
© Zan Markan 2016 - @zmarkan
More jUnit3 goodness • overriding setUp() and tearDown() • testPrefixedMethods()
& test_prefixedMethods() • Ignorance Inheritance is bliss © Zan Markan 2016 - @zmarkan
Welcome to the present • jUnit4 syntax • No more
extending • @Test, @Before, @After, @AfterClass,... • ActivityTestRule, InstrumentationRegistry © Zan Markan 2016 - @zmarkan
What else is in there? • Espresso • More Espresso
(there's a lot to it) • UIAutomator • Test Runner • Test Rules • ... © Zan Markan 2016 - @zmarkan
What else is in there? © Zan Markan 2016 -
@zmarkan
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
Gradle • Set your test runner to be AndroidJUnitRunner •
Add dependencies • Voila! © Zan Markan 2016 - @zmarkan
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
Gradle • Set your test runner to be AndroidJUnitRunner •
Add dependencies • Voila! • Resolve dependencies © Zan Markan 2016 - @zmarkan
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
Resolve with • Force dependency versions in the test APK
• exclude dependency (everywhere applicable) • use Resolution strategy © Zan Markan 2016 - @zmarkan
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
Gradle • Set your test runner to be AndroidJUnitRunner •
Add dependencies • Resolve dependencies • Voila! © Zan Markan 2016 - @zmarkan
© Zan Markan 2016 - @zmarkan
Espresso - components • View interactions & assertions • Hamcrest
syntax • No (unnecessary) waits © Zan Markan 2016 - @zmarkan
Espresso - API Cheat sheet: google.github.io/android-testing-support-library/docs/ espresso/cheatsheet © Zan Markan
2016 - @zmarkan
Poking the screen onView(withId(R.id.button)).perform(click()); © Zan Markan 2016 - @zmarkan
Poking the screen onView(withId(R.id.button)).perform(click()); • allOf, anyOf, ... • withParent,
withText... • isDisplayed, isDialog... © Zan Markan 2016 - @zmarkan
Espresso - contrib • RecyclerView • Drawers • Pickers •
Accessibility © Zan Markan 2016 - @zmarkan
Espresso++ • Custom matchers • Custom ViewActions • Idling resources
• Page objects • Intent mocking © Zan Markan 2016 - @zmarkan
Custom matchers • Find a view in the hierarchy •
Good for custom views & components • Override: • describeTo • matchesSafely © Zan Markan 2016 - @zmarkan
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
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
Custom ViewActions - API public class ScrollableUtils { public static
ViewAction scrollableScrollTo() { return actionWithAssertions(new ScrollableScrollToAction()); } } © Zan Markan 2016 - @zmarkan
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
Implementation... @Override public Matcher<View> getConstraints() { return allOf(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE), isDescendantOfA(anyOf( isAssignableFromClassOrInterface(ScrollingView.class))));
} © Zan Markan 2016 - @zmarkan
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
Don't panic. • Take something that works • ...like scrollTo()
in regular Espresso for ScrollView • modify it • profit © Zan Markan 2016 - @zmarkan
Custom IdlingResource • A better way to wait • Use
when you have background stuff going on • Override: • getName • isIdleNow • registerIdleTransitionCallback © Zan Markan 2016 - @zmarkan
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
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 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 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
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
To boldly go... © Zan Markan 2016 - @zmarkan
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
UI Automator viewer © Zan Markan 2016 - @zmarkan
UI Automator syntax UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation(); device.pressHome(); device.findObject(new UiSelector().descriptionContains("Google
search")).clickAndWaitForNewWindow(); © Zan Markan 2016 - @zmarkan
Test Runner © Zan Markan 2016 - @zmarkan
Test Runner • Spins up tests... • and runs them
• Customise to prepare mocks • Easier run/debug via command line © Zan Markan 2016 - @zmarkan
Extending the Runner • Use custom Application class • Provide
mocked dependencies • Specify this new runner in build.gradle • Kotlin Test Runner © Zan Markan 2016 - @zmarkan
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
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
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
Test Rules © Zan Markan 2016 - @zmarkan
Test Rules • Set starting activity / Service • Replace
ActivityInstrumentationTestCase2 • (in most cases) • Add / Extend to create more components © Zan Markan 2016 - @zmarkan
Test Rules - examples • MockWebServerRule - sets up MockWebServer
when required • Source: github.com/artem-zinnatullin/ qualitymatters © Zan Markan 2016 - @zmarkan
© Zan Markan 2016 - @zmarkan
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
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
Above & Beyond? • Espresso Web for WebViews • JankTestHelper
• Stuff is being added. © Zan Markan 2016 - @zmarkan
Best of all? © Zan Markan 2016 - @zmarkan
It's all open source! © Zan Markan 2016 - @zmarkan
Pusher /ˈpʊʃ ər/ noun 1. Platform of APIs for building
highly connected apps 2. Hiring in London ! © Zan Markan 2016 - @zmarkan
fin • ! www.spacecowboyrocketcompany.com • " @zmarkan • # zan
at markan dot me • $ androidchat.co (zmarkan) • %& @zmarkan © Zan Markan 2016 - @zmarkan
© Zan Markan 2016 - @zmarkan
• 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
• Espresso: Brian Legate (Flickr) • Hyperdrive: Youtube • Road
Runner: BrownZelip (Flickr) • Back to the future: Youtube • Titanic / unit tests passing: Twitter © Zan Markan 2016 - @zmarkan