Pro Yearly is on sale from $80 to $50! »

Lovely Testing of Android Apps

Lovely Testing of Android Apps

Effective and safe auto testing of Android apps

A3d9cdb2c6e32983f1829fcf8e7305f0?s=128

Alex Korovyansky

October 28, 2014
Tweet

Transcript

  1. Lovely Testing of Android Apps Effective and safe auto testing

    of Android apps. Alex Korovyansky #{MBLT}DEV October 28, 2014
  2. Do we need auto testing 
 for our Android apps?

  3. Yes, we do! If we want to be successful in

    long term. But, it should be safe (no flaky tests and buggy tools) and effective (easy to extend, fast to write, fast to run)!
  4. Problems

  5. • Complexity of mobile UI testing Problems

  6. • Complexity of mobile UI testing • Mobile artefacts (GPS,

    sensors, etc) Problems
  7. • Complexity of mobile UI testing • Mobile artefacts (GPS,

    sensors, etc) • Huge number of different devices Problems
  8. • Complexity of mobile UI testing • Mobile artefacts (GPS,

    sensors, etc) • Huge number of different devices • Big number of different approaches Problems
  9. • Complexity of mobile UI testing • Mobile artefacts (GPS,

    sensors, etc) • Huge number of different devices • Big number of different approaches • Lack of official documentation Problems
  10. • Complexity of mobile UI testing • Mobile artefacts (GPS,

    sensors, etc) • Huge number of different devices • Big number of different approaches • Lack of official documentation • Official solution is friend of slow and flaky tests Problems
  11. • Complexity of mobile UI testing • Mobile artefacts (GPS,

    sensors, etc) • Huge number of different devices • Big number of different approaches • Lack of official documentation • Official solution is friend of slow and flaky tests • … Problems
  12. Solutions

  13. • Instrumentation Framework (by Google) Solutions

  14. • Instrumentation Framework (by Google) • Robotium (by Jayway) Solutions

  15. • Instrumentation Framework (by Google) • Robotium (by Jayway) •

    UIAutomator (by Google) Solutions
  16. • Instrumentation Framework (by Google) • Robotium (by Jayway) •

    UIAutomator (by Google) • Robolectric (by ExtremeLabs) Solutions
  17. • Instrumentation Framework (by Google) • Robotium (by Jayway) •

    UIAutomator (by Google) • Robolectric (by ExtremeLabs) • Espresso (by Google) Solutions
  18. • Instrumentation Framework (by Google) • Robotium (by Jayway) •

    UIAutomator (by Google) • Robolectric (by ExtremeLabs) • Espresso (by Google) • Double-Espresso (by Jake Wharton) Solutions
  19. • Instrumentation Framework (by Google) • Robotium (by Jayway) •

    UIAutomator (by Google) • Robolectric (by ExtremeLabs) • Espresso (by Google) • Double-Espresso (by Jake Wharton) • … AssertJ-Android, Burst, Spoon … Solutions
  20. Robolectric Espresso S V Watch a movie — http://www.imdb.com/title/tt0892782/

  21. Double Espresso

  22. • Battery • Location • Mobile network • Multitouch •

    Sensors • … How test Android artefacts?
  23. • Dagger <- currently best one solution for DI @

    Android • Dagger 2 * • RoboGuice • Guice • PicoContainer • Spring • … Use Dependency Injection! Component <<interface>> Service ServiceImpl Assembler <<create>> Watch Introduction to Dagger — http://youtu.be/tPs1e3dQ6FU
  24. Dependency Injection Auto Testing &

  25. • Simple app for registration to #MBLTDev • RESTful communication

    with backend • Location helper for city field • Open Source code • End-to-end auto tests • Just demo app, fork it on GitHub Meet Demo App
  26. • Form fields behaviour • Behaviour of auto-locate button •

    Transformation of form data to request • End-to-end user scenarios:
 1. Successful registration
 2. Registration is closed error
 3. Network error • Test on different devices What we can test?
  27. Setup Double Espresso ! androidTestCompile 'com.jakewharton.espresso:espresso:1.1-r3'' { exclude group: 'com.squareup.dagger'

    } ! defaultConfig { testInstrumentationRunner "com.google.android.apps.common.testing" + ".testrunner.GoogleInstrumentationTestRunner" } ! build.gradle packagingOptions { exclude ‘LICENSE.txt' }
  28. public class MBLTDevDemoApp extends Application { ! @Override public void

    onCreate() { super.onCreate(); setModules(getStartModules()); } ! public void inject(Object component) { this.objectGraph.inject(component); } ! public Object[] getModules() { return modules.toArray(new Object[modules.size()]); } ! public void plusModule(Object module) { this.modules.add(module); this.objectGraph = objectGraph.plus(module); } ! public void setModules(Object[] modules) { this.modules = new ArrayList<Object>(Arrays.asList(modules)); this.objectGraph = ObjectGraph.create(modules); } ! protected Object[] getStartModules() { return Modules.list(this); } } Daggerized app
  29. Daggerize test app public class MBLTDevDemoTestApp extends MBLTDevDemoApp { @Override

    protected Object[] getStartModules() { return new Object[]{new MBLTDevDemoModule(this), new MBLTDevDemoTestModule()}; } } @Module( injects = { GetTicketActivity.class, … } ) public class MBLTDevDemoTestModule { ! @Provides @Singleton public LocationService provideLocationService() { return new TestLocationService(); } ! @Provides @Singleton public TestLocationService provideLocationService( LocationService locationService) { return locationService; } ! … } @Module( complete = false, library = true, overrides = true, injects = { DemoTests.class} ) public class MBLTDevDemoModule { ! @Provides @Singleton public LocationService provideLocationService( Context context) { return new RealLocationService(context); } ! … }
  30. Customize TestRunner package com.medlert.android.transport.env; ! … ! public class MBLTDevDemoTestRunner

    extends GoogleInstrumentationTestRunner { ! @Override public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { return Instrumentation.newApplication(MBLTDevDemoTestApp.class, context); } ! } ! defaultConfig { testInstrumentationRunner “com.mbltdev.lovelyandroid.env.testrunner” } ! build.gradle
  31. DI in super test public class MBLTDevDemoInstrumentationTestCase2<T extends Activity> extends

    ActivityInstrumentationTestCase2<T> { ! private Object[] modules; ! public MBLTDevDemoInstrumentationTestCase2(Class<T> activityClass) { super(activityClass); } ! @Override protected void setUp() throws Exception { super.setUp(); this.modules = getApp().getModules(); getApp().inject(this); } ! @Override protected void tearDown() throws Exception { getApp().setModules(modules); super.tearDown(); } ! protected MBLTDevDemoApp getApp() { return (MBLTDevDemoApp) (getInstrumentation().getTargetContext().getApplicationContext()); } }
  32. First cup of coffee public class InitialStateTests extends MBLTDevDemoInstrumentationTestCase2<GetTicketActivity> {

    ! public InitialStateTests() { super(GetTicketActivity.class); } ! @Override protected void setUp() throws Exception { super.setUp(); getActivity(); } ! @SmallTest public void testFormInitialState() throws Exception { onView(withId(R.id.first_name_field)).check(matches(isDisplayed())); onView(withId(R.id.last_name_field)).check(matches(isDisplayed())); onView(withId(R.id.city_field)).check(matches(isDisplayed())); onView(withId(R.id.company_field)).check(matches(isDisplayed())); onView(withId(R.id.position_field)).check(matches(isDisplayed())); onView(withId(R.id.get_ticket)).check(matches(isDisplayed())); } ! }
  33. Coffee with Dependency Injection public class BehaviourTests extends MBLTDevDemoInstrumentationTestCase2<GetTicketActivity> {

    ! @Inject TestLocationService testLocationService; @Inject TestBackendService testBackendService; @Inject @TestOperationLengthMs long testOperationLengthMs; … ! @SmallTest public void testAutoLocateButton() throws Exception { testLocationService.setLocation(TestLocations.moscow()); getActivity(); onView(withId(R.id.auto_locate)).perform(click()); Thread.sleep(testOperationLengthMs); onView(withId(R.id.field_city)).check(matches(withText(“Moscow”))); } ! @SmallTest public void testFormNetworkRequest() throws Exception { Request chuckNorrisRequest = TestRequests.chuckNorris(); onFillForm(chuckNorrisRequest); onView(withId(R.id.get_ticket)).perform(click()); assertEquals(chuckNorrisRequest, testBackendService.getLastRequest()); } ! }
  34. Cup with end-to-end user scenarios public class EndToEndTests extends MBLTDevDemoInstrumentationTestCase2<GetTicketActivity>

    { @Inject TestBackendService testBackendService; @Inject @TestOperationLengthMs testOperationLengthMs; … ! @SmallTest public void testSuccessfulRegistration() throws Exception { testBackendService.setGetTicketResponse(Response.success()); getActivity(); onFillForm(TestRequests.chuckNorris()); onView(withId(R.id.get_ticket)).perform(click()); Thread.sleep(testOperationLengthMs); onView(withText(“Congrats! You are registered!”)).check(isDisplayed()); } ! @SmallTest public void testClosedRegistration() throws Exception { testBackendService.setGetTicketResponse(Response.alreadyClosed()); getActivity(); onFillForm(TestRequests.chuckNorris()); onView(withId(R.id.get_ticket)).perform(click()); Thread.sleep(testOperationLengthMs); onView(withText(“Oops… Registration is already closed!”)).check(isDisplayed()); } ! }
  35. Run Tests!

  36. • Spoon — https://github.com/square/spoon • SauceLabs — https://saucelabs.com/ • TestObject

    — https://testobject.com/ • Appurify — http://appurify.com/ • Appthwack — http://appthwack.com/ • TestDroid — http://testdroid.com • … Testing on different real devices
  37. Conclusions • It’s good time to start testing of your

    apps • Hope, you will love it • Select Double Espresso or Robolectric • Dependency Injection and Test Automation are big friends • More things is coming • Keep your hand on a pulse!
  38. Links 101 • Espresso
 https://code.google.com/p/android-test-kit/wiki/Espresso • Double Espresso
 https://github.com/JakeWharton/double-espresso •

    Robolectric
 https://github.com/robolectric/robolectric • Dagger
 https://github.com/square/dagger • Demo App
 https://github.com/AlexKorovyansky/mbltdev-demo
  39. Watch and talk • Introduction to Espresso
 http://youtu.be/T7ugmCuNxDU • Introduction

    to Dagger
 http://youtu.be/tPs1e3dQ6FU • GTAC 2013
 http://bit.ly/gtac2013 • GTAC 2014
 http://bit.ly/gtac2013 • Make a talk in GDG RU
 http://bit.ly/gdgru_online_talk
  40. Q&A 
 Alex Korovyansky korovyansk@gmail.com http://about.me/korovyansk