How to build rock-solid apps &"keep"100m+"users"happy" Iordanis)Giannakakis)) iordanis_g " Savvas ) Dalkitsis )) @ geeky_android "

qconlondon "

How Shazam works

Some numbers

Happy users Even developers?

Android testing

Faster release cycles

Better code

Cheaper http://testdroid.com/testdroid/5851

Easy

A user walks into a bar

Doing it the test driven way "" "" Write"UI" test" Implement" Refactor " Write"unit"test" BDD" TDD"

Test first •   Generally easier •   Eliminates external dependencies •   Easily repeatable

The Acceptance Tests cycle "" "" Write"UI" test" Implement" Refactor " Write"unit"test" BDD" TDD"

Acceptance criteria •   Given: arrange •   When: act •   Then: assert

Example Given a user is near a music venue And the server always returns a known result

When the user Shazams

Then the user can check-in their discovery

gwen

!given(user). isNear ( lexington ());! !given(server).returns( lustForLife ());! !! !when(user). shazams ();! ! !then(user). canCheckIn ( lustForLife (),! lexington ());! http://github.com/shazam/gwen

Other libraries for Acceptance Tests •   Instrumentation •   JUnit 3 •   Espresso & Robotium

•   Fork •   HamMock Server

The Unit tests cycle "" "" Write" UI"test" Implement" Refactor " Write"unit" test" BDD" TDD"

What we don’t test Activities & Fragments http://github.com/xxv/android-lifecycle

What we don’t test

What we do test

•   Presentation logic •   Business logic •   That’s it

The MVP pattern

Presenter Model View

The MVP pattern

Presenter Model View Android

The MVP pattern

•   Makes presentation logic testable •   No need to test “dummy” view •   Avoid Android dependencies

Dependency Injection, Yourself

•   Breaks hardcoded dependencies •   Behaviour

vs Implementation •   Implementations for test & runtime

Feature X Hardcoded dependencies

Client Feature X

Dependency Injection

Client Feature X Interface Injector"

Model !public ! interface ! VenueRetriever !{! !!!! void ! findClosestVenue ( VenueFoundCallback ! callback );! !}! ! !public ! class ! NetworkVenueRetriever ! implements! VenueRetriever !{ ! !!!!public ! void ! findClosestVenue ( VenueFoundCallback ! callback )!{! !!!!!!!//!Some!slow!networking! !!!!}! !}! ! !public ! class ! LocalVenueRetriever ! implements! VenueRetriever !{ ! !!!!public ! void ! findClosestVenue ( VenueFoundCallback ! callback )!{! !!!!!!!//!DB!lookJup!/!caching!layer,!perhaps?! !!!!}! !} "

Activity public!class ! ResultActivity ! extends !Activity! implements ! ResultView !{! !!private ! final ! VenueRetriever ! venueRetriever ;! !! private ! ResultPresenter ! resultPresenter ;! ! !! public ! ResultActivity ()!{! !!!! venueRetriever !=! venueRetriever ();, !!}! ! !! public ! void ! onCreate (Bundle! savedInstanceState )!{! !!!!//! TODO: !Setup!layouts!&!views! ! !!!!Result!result!=! resultToDisplay (); ! !!!! resultPresenter

new ! ResultPresenter ( this ,! venueRetriever ,!result);! !!}! ! !! public ! void ! onStart ()!{! !!!! resultPresenter .startPresenting ();! !!}! } "

Presenter !public ! class ! ResultPresenter !{! ! !!!! public ! ResultPresenter ( ResultView ! resultView ,! VenueRetriever !! !!!!!!!!!! venueRetriever ,!Result!result)!{! !!!!!!! this . resultView !=! resultView ;! !!!!!!! this . venueRetriever !=! venueRetriever ;! !!!!!!! this . result !=!result;! !!!!}! ! !!!! public ! void ! startPresenting ()!{! !!!!!!! resultView .showResult ( result );! !!!!!!! venueRetriever .findClosestVenue ( new ! VenueFoundCallback ()!{! !!!!!!!!!! public ! void ! venueFound (Venue!venue)!{! !!!!!!!!!!!!! resultView .showCheckInPrompt (venue);! !!!!!!!!!!}! !!!!!!!});! !!!!}! !} "

View !public ! interface ! ResultView !{! ! !!!! void ! showResult (Result!track);! ! !!!! void ! showCheckInPrompt (Venue!venue);! !}!

Activity !public ! class ! ResultActivity ! extends !Activity! implements ! ResultView !{! ! !!!! public ! void ! showResult (Result!result)!{! !!!!!!!// TODO !show!the!result!screen!&!bind!result!data! !!!!}! ! !!!! public ! void ! showCheckInPrompt (Venue!venue)!{! !!!!!!!// TODO !bind!the!venue!with!checkJin!prompt!view! !!!!}! !} "

Other Unit Test technologies •   JUnit 4 •   Robolectric

java.lang.RuntimeException : Stub!

•   Hamcrest

•   JMock

Test execution

Continuous Integration

Speed (Hint: slow)

Usual execution Test"Suite"

Spoon http://github.com/square/spoon

Fork http://github.com/shazam/fork

Test"Suite"

Example

Fork http://github.com/shazam/fork

Fork http://github.com/shazam/fork

•   Pooled execution •   Infinitely scalable •   Current setup 1 test / 2 seconds

Flakiness monitor

ADB Remote http://github.com/sleekweasel/CgiAdbRemote

If all else fails

If all else fails

If all else fails

Summary •   Testing is easier than you may think •   Practice, practice, practice •   Toolset is limited but getting better •   Ship it!

Questions?