How we Build Rock-solid Apps and Keep 100M+ Users Happy at Shazam

A presentation at QCon London 2015 in March 2015 in London, UK by Savvas Dalkitsis

Slide 1

Slide 1

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

qconlondon "

Slide 2

Slide 2

How Shazam works

Slide 3

Slide 3

Some numbers

Slide 4

Slide 4

Happy users Even developers?

Slide 5

Slide 5

Android testing

Slide 6

Slide 6

Faster release cycles

Slide 7

Slide 7

Better code

Slide 8

Slide 8

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

Slide 9

Slide 9

Easy

Slide 10

Slide 10

A user walks into a bar

Slide 11

Slide 11

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

Slide 12

Slide 12

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

Slide 13

Slide 13

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

Slide 14

Slide 14

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

Slide 15

Slide 15

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

Slide 16

Slide 16

gwen

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

Slide 17

Slide 17

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

•   Fork •   HamMock Server

Slide 18

Slide 18

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

Slide 19

Slide 19

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

Slide 20

Slide 20

What we don’t test

Slide 21

Slide 21

What we do test

•   Presentation logic •   Business logic •   That’s it

Slide 22

Slide 22

The MVP pattern

Presenter Model View

Slide 23

Slide 23

The MVP pattern

Presenter Model View Android

Slide 24

Slide 24

The MVP pattern

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

Slide 25

Slide 25

Dependency Injection, Yourself

•   Breaks hardcoded dependencies •   Behaviour

vs Implementation •   Implementations for test & runtime

Slide 26

Slide 26

Feature X Hardcoded dependencies

Client Feature X

Slide 27

Slide 27

Dependency Injection

Client Feature X Interface Injector"

Slide 28

Slide 28

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?! !!!!}! !} "

Slide 29

Slide 29

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 ();! !!}! } "

Slide 30

Slide 30

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);! !!!!!!!!!!}! !!!!!!!});! !!!!}! !} "

Slide 31

Slide 31

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

Slide 32

Slide 32

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! !!!!}! !} "

Slide 33

Slide 33

Other Unit Test technologies •   JUnit 4 •   Robolectric

java.lang.RuntimeException : Stub!

•   Hamcrest

•   JMock

Slide 34

Slide 34

Test execution

Slide 35

Slide 35

Continuous Integration

Slide 36

Slide 36

Speed (Hint: slow)

Slide 37

Slide 37

Usual execution Test"Suite"

Slide 38

Slide 38

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

Slide 39

Slide 39

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

Test"Suite"

Slide 40

Slide 40

Example

Slide 41

Slide 41

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

Slide 42

Slide 42

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

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

Slide 43

Slide 43

Flakiness monitor

Slide 44

Slide 44

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

Slide 45

Slide 45

If all else fails

Slide 46

Slide 46

If all else fails

Slide 47

Slide 47

If all else fails

Slide 48

Slide 48

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

Slide 49

Slide 49

Questions?