Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MCE2015 - Automated Testing for Modern Android Applications

E0ac1256093c733a5d5a26085b90966f?s=47 Andy Dyer
February 06, 2015

MCE2015 - Automated Testing for Modern Android Applications


Andy Dyer

February 06, 2015


  1. Automated Testing for Modern Android Applications

  2. Andy Dyer +AndrewDyer @dammitandy

  3. None
  4. Making Android apps testable

  5. Dynamic languages Tests shouldn't drive implementation.

  6. Static languages You know nothing.

  7. Making Android apps testable 1. Dependency injection 2. Mocking &

    stubbing 3. Unit & integration tests
  8. Dependency injection

  9. Dependency injection • Classes receive dependencies, don’t have to know

    where to find them or how to create them • Swap components for mocks in tests • Use different components for different build flavors, etc.
  10. Basic dependency injection public class Beer implements Reinheitsgebot { Water

    water; Barley barley; Hops hops; public Beer(Water water, Barley barley, Hops hops) { this.water = water; this.barley = barley; this.hops = hops; } }
  11. Dagger A Java dependency injection library

  12. Dagger • Defining dependencies at compile time avoids reflection at

    runtime • Compiler validates components, modules, and injections • Dagger 2 is currently in alpha, but already being used by Google in production apps google.github.io/dagger/
  13. Dagger Modules @Module public class MyModule { @Provides @Singleton public

    MyService provideMyService() { return new MyService(); } }
  14. Dagger Components @Component(modules = MyModule.class) public interface Graph { void

    inject(Activity activity); void inject(Fragment fragment); public final static class Initializer { public static Graph init(boolean mockMode) { return Dagger_Graph.builder().build(); } } }
  15. Dagger Object Graph public class MyApplication extends Application { @Getter

    static DemoApplication instance; @Getter Graph graph; @Override public void onCreate() { super.onCreate(); instance = this; graph = Graph.Initializer.init(false); } public void setMockMode(boolean useMock) { graph = Graph.Initializer.init(useMock); } }
  16. Dagger dependency injection public class MyFragment extends Fragment { @Inject

    MyService service; @Override public void onViewCreated(View view, Bundle savedInstanceState) { MyApplication.getInstance().getGraph().inject(this); service.getMyData(); } }
  17. Learning more about Dagger • Jake Wharton - Dependency Injection

    with Dagger 2 parleys.com/play/5471cdd1e4b065ebcfa1d557 • Gregory Kick - Dagger 2: A New Type of Dependency Injection youtube.com/watch?v=oK_XtfXPkqw
  18. Mocking & Stubbing

  19. Mocking & Stubbing • Substitute runtime implementation for something that

    can be predictably tested in isolation • Verify behavior
  20. A Java mocking library

  21. Mockito • Mock/stub dependencies and function return values • Inject

    mocks to validate behavior in tests • Use included Hamcrest matchers for clear, readable tests code.google.com/p/mockito/
  22. Using Mockito // create mock MyClass mocked = mock(MyClass.class); //

    specify behavior when(mocked.doSomething()).thenReturn(somethingElse); // verify method calls verify(mocked).getMyData(anyInt(), anyString());
  23. Using Mockito // capture arguments ArgumentCaptor<Callback> captor = ArgumentCaptor.forClass(Callback.class); verify(authenticationService).login(anyString(),

    anyString(), captor.capture()); // simulate error conditions, etc. captor.getValue().failure(RetrofitError.unexpectedError( "Invalid password", new Exception()));
  24. Unit & Integration Tests

  25. Tests or it didn't happen

  26. Unit testing public class BeerTest extends InstrumentationTestCase { @Inject Beer

    beer; @Override protected void setUp() throws Exception { MyApplication.getInstance().getGraph().inject(this); } public void testBeerIsGood() { assertTrue(beer.isGood()); } }
  27. An Android UI testing library

  28. Espresso • Handles activity creation & state sync • Simple,

    concise API • Really fast! code.google.com/p/android-test-kit*
  29. UI testing with Espresso public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity> {

    public MyActivityTest() { super(MyActivity.class); } @Override protected void setUp() throws Exception { super.setUp(); getActivity(); // trigger activity launch } public void testInvalidEmailShowsError() { onView(withId(R.id.email)).perform(typeText("abc"), closeSoftKeyboard()); onView(withId(R.id.email_sign_in_button)).perform(click()); onView(withId(R.id.email)).check(matches(withError( getActivity().getString(R.string.error_invalid_email)))); } }
  30. Sample application • Dagger object graph • Retrofit API with

    sample request • Lombok & Android Studio plugin • Login activity • Activity with fragment to make API request and display data
  31. Testing the login activity • Simulate data entry and taps

    to validate UI functionality • Simulate an error without making a network request
  32. Testing the main activity • Verify the RecyclerView contains data

    • Simulate taps to validate the appropriate URLs are loaded in a WebView
  33. Testing the API • Use mock API response loaded from

    text file to validate JSON parsing
  34. Demo

  35. Known issues • Some tests may fail on older devices/emulators

    such as those running < API 18. Hopefully this will be fixed soon by an update to the test support library.
  36. Additional resources • Bryan Stern/Circle Engineering - Instrumentation Testing with

    Dagger, Mockito, and Espresso engineering.circle.com/instrumentation-testing-with-dagger- mockito-and-espresso
  37. Conclusion Using dependency injection, mocking, and automated testing tools helps

    us build better apps. Working together as a community, we can make testing even easier.
  38. Questions?

  39. Slides speakerdeck.com/abdyer/mce2015-automated-testing-for- modern-android-applications Code github.com/abdyer/android-test-demo/releases/tag/ mceconf-2015 +AndrewDyer @dammitandy