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

MCE2015 - Automated Testing for Modern Android Applications

Andy Dyer
February 06, 2015

MCE2015 - Automated Testing for Modern Android Applications

Andy Dyer

February 06, 2015
Tweet

More Decks by Andy Dyer

Other Decks in Programming

Transcript

  1. 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.
  2. 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; } }
  3. 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/
  4. Dagger Modules @Module public class MyModule { @Provides @Singleton public

    MyService provideMyService() { return new MyService(); } }
  5. 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(); } } }
  6. 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); } }
  7. 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(); } }
  8. 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
  9. Mocking & Stubbing • Substitute runtime implementation for something that

    can be predictably tested in isolation • Verify behavior
  10. 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/
  11. Using Mockito // create mock MyClass mocked = mock(MyClass.class); //

    specify behavior when(mocked.doSomething()).thenReturn(somethingElse); // verify method calls verify(mocked).getMyData(anyInt(), anyString());
  12. 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()));
  13. 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()); } }
  14. Espresso • Handles activity creation & state sync • Simple,

    concise API • Really fast! code.google.com/p/android-test-kit*
  15. 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)))); } }
  16. 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
  17. Testing the login activity • Simulate data entry and taps

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

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

    text file to validate JSON parsing
  20. 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.
  21. Additional resources • Bryan Stern/Circle Engineering - Instrumentation Testing with

    Dagger, Mockito, and Espresso engineering.circle.com/instrumentation-testing-with-dagger- mockito-and-espresso
  22. 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.