Android TDD &Marcin Gryszko @mgryszko
Newgig
User stories  written
Kickoff?
walking     skeleton“Implementation of thethinnest possible slice ofreal functionality that wecan automatically build,depl...
Iteration “zero”  TDD cycle
01. Understand the problem2. Think about architecture3. Automate
Android testing framework
Architecture
Automate
+ Android  plugin
Failing acceptance testpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {    public vo...
Failing acceptance testpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {    public vo...
Page objectpublic class TimelineDriver {    private Solo solo;    public TimelineDriver(Solo solo) { this.solo = solo; }  ...
Failing acceptance testpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {    private S...
Passing acceptance testpublic class TimelineActivity extends Activity {    public void onCreate(Bundle savedInstanceState)...
Tracer bullet     hit
Let’s ref ac tor
Hamcrestpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {    private void assertTimel...
Hamcrestpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> {    private void assertTimel...
Roboguicepublic class TimelineActivity extends Activity {    private void displayTimeline(List<Chirp> chirps) {        Tim...
Roboguicepublic class TimelineActivity extends RoboActivity {    @InjectView(R.id.timelineView)    private ListView timeli...
Falling integration testpublic class ChirpJsonRepositoryTest extends TestCase {    private ChirpRepository repository = ne...
Passing integration testpublic class ChirpJsonRepository implements ChirpRepository {    public List<Chirp> findTimelineOf...
Passing integration testpublic class TimelineActivity extends Activity {    private List<Chirp> loadTimeline() {        re...
Passing integration testpublic class TimelineActivity extends RoboActivity {    @Inject    private ChirpRepository chirpRe...
Optimization
Async loadingpublic class TimelineActivity extends RoboActivity    implements TimelineLoadListener {    @Inject    private...
Async loadingpublic class TimelineLoadTask extends RoboAsyncTask<List<Chirp>>    implements AsyncTimelineLoader {    publi...
First unit testpublic class TimelineLoadTaskTest extends RoboUnitTestCase<Application> {    private Mockery context = new ...
First unit test    private CountDownLatch taskDone = new CountDownLatch(1);    private TimelineLoadTask createTimelineLoad...
Really unit?
straight JUnitIntelliJjava.lang.RuntimeException: Stub!at junit.runner.BaseTestRunner.(BaseTestRunner.java:5)MavenTests ru...
TestNGIntelliJ===============================================Custom suiteTotal tests run: 1, Failures: 0, Skips: 0========...
Robolectric@RunWith(InjectingTestRunner.class)public class TimelineUnitTest {    @Inject    private TimelineActivity activ...
Wrap-up
Thanks!
https://github.com/mgryszko/android-                 tdd-ci
you              ?  jobs     Marcin   @osoco.es
Image sources:http://www.flickr.com/photos/sarah_mccans/219287847/http://www.flickr.com/photos/f-oxymoron/5005146417/http://...
Android TDD & CI
Upcoming SlideShare
Loading in...5
×

Android TDD & CI

2,804

Published on

How to practice TDD (and Acceptance TDD) when developing Android applications.

Covers usage of Robotium, Roboguice, Hamcrest, JMock and Robolectric.

Published in: Technology, Business
0 Comments
9 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,804
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
67
Comments
0
Likes
9
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Android TDD & CI

    1. 1. Android TDD &Marcin Gryszko @mgryszko
    2. 2. Newgig
    3. 3. User stories written
    4. 4. Kickoff?
    5. 5. walking skeleton“Implementation of thethinnest possible slice ofreal functionality that wecan automatically build,deploy, and test end-to-end”original idea by Alistair Cockburn
    6. 6. Iteration “zero” TDD cycle
    7. 7. 01. Understand the problem2. Think about architecture3. Automate
    8. 8. Android testing framework
    9. 9. Architecture
    10. 10. Automate
    11. 11. + Android plugin
    12. 12. Failing acceptance testpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { public void test_chirps_are_displayed_timely_ordered() throws Exception { timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size()); assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps()); }}
    13. 13. Failing acceptance testpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { public void test_chirps_are_displayed_timely_ordered() throws Exception { timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size()); assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps()); }}
    14. 14. Page objectpublic class TimelineDriver { private Solo solo; public TimelineDriver(Solo solo) { this.solo = solo; } public void waitUntilTimelineLoaded(int timelineSize) { solo.waitForView(TableRow.class, timelineSize, 5000); } public List<Chirp> getDisplayedTimeline() { ListView timelineView = (ListView) solo.getView(R.id.timelineView); List<Chirp> chirps = new ArrayList<Chirp>(); for (int i = 0; i < timelineView.getCount(); i++) { chirps.add((Chirp) timelineView.getItemAtPosition(i)); } return chirps; }}
    15. 15. Failing acceptance testpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { private Solo solo; private TimelineDriver timelineDriver; protected void setUp() throws Exception { super.setUp(); solo = new Solo(getInstrumentation(), getActivity()); timelineDriver = new TimelineDriver(solo); } public void test_chirps_are_displayed_timely_ordered() throws Exception { timelineDriver.waitUntilTimelineLoaded(mgryszkosChirps().size()); assertTimelineEqualTo(timelineDriver.getDisplayedTimeline(), mgryszkosChirps()); } private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) { assertEquals(expected, actual); }}
    16. 16. Passing acceptance testpublic class TimelineActivity extends Activity { public void onCreate(Bundle savedInstanceState) { // super.onCreate & setContentView omitted List<Chirp> chirps = loadTimeline(); displayTimeline(chirps); } private List<Chirp> loadTimeline() { return executeJsonRequest("http://...", "mgryszko"); } private List<Chirp> executeJsonRequest(String uri, Object... requestParams) { RestTemplate restTemplate = new RestTemplate(); return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams)); } private void displayTimeline(List<Chirp> chirps) { TimelineAdapter adapter = new TimelineAdapter(); adapter.setActivity(this); adapter.setChirps(chirps); ListView timelineView = (ListView) findViewById(R.id.timelineView); timelineView.setAdapter(adapter); }}
    17. 17. Tracer bullet hit
    18. 18. Let’s ref ac tor
    19. 19. Hamcrestpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) { assertEquals(expected, actual); }}
    20. 20. Hamcrestpublic class TimelineTest extends ActivityInstrumentationTestCase2<TimelineActivity> { private void assertTimelineEqualTo(List<Chirp> actual, List<Chirp> expected) { assertThat(actual.size(), equalTo(expected.size())); for (Chirp chirp : expected) { assertThat(format("Chirp %s not in the timeline", chirp), actual, hasItem(chirp)); } }}
    21. 21. Roboguicepublic class TimelineActivity extends Activity { private void displayTimeline(List<Chirp> chirps) { TimelineAdapter adapter = new TimelineAdapter(); adapter.setActivity(this); adapter.setChirps(chirps); ListView timelineView = (ListView) findViewById(R.id.timelineView); timelineView.setAdapter(adapter); }}
    22. 22. Roboguicepublic class TimelineActivity extends RoboActivity { @InjectView(R.id.timelineView) private ListView timelineView; private void displayTimeline(List<Chirp> chirps) { TimelineAdapter adapter = getInjector() .getInstance(TimelineAdapter.class) .withChirps(chirps)); timelineView.setAdapter(adapter); }}
    23. 23. Falling integration testpublic class ChirpJsonRepositoryTest extends TestCase { private ChirpRepository repository = new ChirpJsonRepository(); public void test_returns_the_timeline_of_a_chirper() { assertTimelineEqualTo(repository.findTimelineOf("mgryszko"), mgryszkosChirps()); }}
    24. 24. Passing integration testpublic class ChirpJsonRepository implements ChirpRepository { public List<Chirp> findTimelineOf(String chirper) { return executeJsonRequest("http://...", chirper); } private List<Chirp> executeJsonRequest(String uri, Object... requestParams) { RestTemplate restTemplate = new RestTemplate(); return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams)); }}
    25. 25. Passing integration testpublic class TimelineActivity extends Activity { private List<Chirp> loadTimeline() { return executeJsonRequest("http://...", "mgryszko"); } private List<Chirp> executeJsonRequest(String uri, Object... requestParams) { RestTemplate restTemplate = new RestTemplate(); return asList(restTemplate.getForObject(uri, Chirp[].class, requestParams)); }}
    26. 26. Passing integration testpublic class TimelineActivity extends RoboActivity { @Inject private ChirpRepository chirpRepository; private List<Chirp> loadTimeline() { return chirpRepository.findTimelineOf("mgryszko"); }}
    27. 27. Optimization
    28. 28. Async loadingpublic class TimelineActivity extends RoboActivity implements TimelineLoadListener { @Inject private AsyncTimelineLoader timelineLoader; private void loadTimeline() { timelineLoader.loadChirperTimeline("mgryszko", this); } public void timelineLoaded(List<Chirp> timeline) { displayTimeline(timeline); }}public interface TimelineLoadListener { void timelineLoading(); void timelineLoaded(List<Chirp> timeline);}
    29. 29. Async loadingpublic class TimelineLoadTask extends RoboAsyncTask<List<Chirp>> implements AsyncTimelineLoader { public void loadChirperTimeline(String chirper, TimelineLoadListener loadListener) { this.execute(); } @Override protected void onPreExecute() throws Exception { loadListener.timelineLoading(); } public List<Chirp> call() throws Exception { return repository.findTimelineOf(chirper); } @Override protected void onSuccess(List<Chirp> chirps) throws Exception { loadListener.timelineLoaded(chirps); }}
    30. 30. First unit testpublic class TimelineLoadTaskTest extends RoboUnitTestCase<Application> { private Mockery context = new Mockery(); private ChirpRepository repository = context.mock(ChirpRepository.class); private TimelineLoadListener loadListener = context.mock(TimelineLoadListener.class); public void test_loads_timeline_successfully_and_notifies_the_listener() { TimelineLoadTask task = createTimelineLoadTask(); context.checking(new Expectations() {{ List<Chirp> timeline = asList(A_CHIRP); oneOf(loadListener).timelineLoading(); oneOf(repository).findTimelineOf(CHIRPER); will(returnValue(timeline)); oneOf(loadListener).timelineLoaded(with(timeline)); }}); executeInFakeUIThread(task); }}
    31. 31. First unit test private CountDownLatch taskDone = new CountDownLatch(1); private TimelineLoadTask createTimelineLoadTask() { return new TimelineLoadTask(repository) { @Override protected void onFinally() { taskDone.countDown(); } }; } private void executeInFakeUIThread(final TimelineLoadTask task) { new RoboLooperThread() { public void run() { task.loadChirperTimeline(CHIRPER, loadListener); } }.start(); taskDone.await(); }}
    32. 32. Really unit?
    33. 33. straight JUnitIntelliJjava.lang.RuntimeException: Stub!at junit.runner.BaseTestRunner.(BaseTestRunner.java:5)MavenTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024sec
    34. 34. TestNGIntelliJ===============================================Custom suiteTotal tests run: 1, Failures: 0, Skips: 0===============================================MavenRunning TestSuiteTests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.298sec
    35. 35. Robolectric@RunWith(InjectingTestRunner.class)public class TimelineUnitTest { @Inject private TimelineActivity activity; @Inject private AsyncTimelineLoaderStub timelineLoader; @Test public void triggers_timeline_loading() { activity.onCreate(NO_SAVED_INSTANCE_STATE); assertThat(timelineLoader.isTimelineLoaded(), is(true)); } @Test public void progress_dialog_is_displayed_when_timeline_is_loaded() { activity.timelineLoading(); assertThat(Robolectric.shadowOf(activity).getLastShownDialogId(), is(TimelineActivity.PROGRESS_DIALOG_ID)); }}
    36. 36. Wrap-up
    37. 37. Thanks!
    38. 38. https://github.com/mgryszko/android- tdd-ci
    39. 39. you ? jobs Marcin @osoco.es
    40. 40. Image sources:http://www.flickr.com/photos/sarah_mccans/219287847/http://www.flickr.com/photos/f-oxymoron/5005146417/http://www.bluebison.net/content/2007/a-skeleton-walking-his-pets/http://www.flickr.com/photos/familymwr/5009855774/http://www.flickr.com/photos/zbraineater/2213219097/http://www.flickr.com/photos/stusev/3296738594/http://www.flickr.com/photos/hadamsky/293310259/http://www.flickr.com/photos/qthomasbower/3392847831/http://www.flickr.com/photos/mikecogh/5113779851/
    1. ¿Le ha llamado la atención una diapositiva en particular?

      Recortar diapositivas es una manera útil de recopilar información importante para consultarla más tarde.

    ×