how to build rock solid apps and keep 100m+ users happy
DESCRIPTION
TRANSCRIPT
How to build rock-solid apps and keep 100m+ users happy
Iordanis Giannakakis
About me
• Iordanis “Jordan” Giannakakis
• Android Team Lead at Shazam
• @iordanis_g
@iordanis_g
How Shazam works
@iordanis_g
100+ million users
@iordanis_g
Happy users
@iordanis_g
Even developers?
Android testing
@iordanis_g
Faster release cycles
@iordanis_g
Better code
@iordanis_g
Easy
@iordanis_g
A user walks into a bar...
@iordanis_g
Doing it the test-driven way
@iordanis_g
Write UI test
Implement
Refactor
Write unit test
BDD
TDD
“To TDD or not to TDD?
@iordanis_g
...That is not the question” Seb Rose
http://claysnow.co.uk/to-tdd-or-not-to-tdd/
Test-first
@iordanis_g
• Generally easier
• Eliminates external dependencies
• Easily repeatable
The Acceptance tests cycle
@iordanis_g
Write UI test
Implement
Refactor
Write unit test
BDD
TDD
Acceptance criteria
@iordanis_g
Given: arrange
When: act
Then: assert
Example
@iordanis_g
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
@iordanis_g
given(user).isNear(lexington())
given(server).returns(lustForLife())
when(user).shazams()
then(user).canCheckIn(lustForLife(), lexington())
https://github.com/shazam/gwen
Acceptance tests technologies
@iordanis_g
• Instrumentation
• JUnit 3
• Robotium & Espresso
• gwen
• HamMock Server
The Unit tests cycle
@iordanis_g
Write UI test
Implement
Refactor
Write unit test
BDD
TDD
What we don’t test
@iordanis_g
Activities & Fragments
https://github.com/xxv/android-lifecycle/
What we don’t test
@iordanis_g
What we do unit test
@iordanis_g
• Presentation logic
• Business logic
• That’s all!
The MVP pattern
@iordanis_g
Presenter
Model View
The MVP pattern, for Android
@iordanis_g
Presenter
Model
View
start
stop
Android
The MVP pattern
@iordanis_g
• Makes presentation logic testable
• No need to test the “slave” view
• Avoid Android dependencies
Dependency Injection, Yourself (DIY)
@iordanis_g
• Break hardcoded dependencies
• Behaviour vs implementation
• Swappable dependencies for test and production runtimes
Hard-coded dependencies
@iordanis_g
Client
Feature X Feature X
Dependency Injection
@iordanis_g
Client
Injector
Feature X Interface
Model
@iordanis_g
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 look-up / caching layer, perhaps?
}
}
Activity
@iordanis_g
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
@iordanis_g
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
@iordanis_g
public interface ResultView {
void showResult(Result track);
void showCheckInPrompt(Venue venue);
}
Activity
@iordanis_g
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 check-in prompt view
}
}
Unit tests technologies
@iordanis_g
• JUnit 4
• Robolectric java.lang.RuntimeException: Stub!
• Hamcrest
• JMock
Test execution
@iordanis_g
Continuous Integration
@iordanis_g
Speed (Hint: slow)
@iordanis_g
Usual execution
@iordanis_g
Test Suite
Fork
@iordanis_g
Test Suite
Fork
@iordanis_g
• Inspired by Spoon, but faster
• Infinitely scalable
• Current setup 1 test / 2 secs
• Pooled execution
Flakiness monitor
@iordanis_g
ADB Remote
@iordanis_g
https://github.com/sleekweasel/CgiAdbRemote
If all else fails...
@iordanis_g
If all else fails...
@iordanis_g
If all else fails...
@iordanis_g
Summary
@iordanis_g
• Testing is easier than you may think
• Practice, practice, practice
• Toolset is limited
• Ship it!
@iordanis_g
Questions?
Hiring...