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. Automated Testing
    for Modern Android
    Applications

    View Slide

  2. Andy Dyer
    +AndrewDyer
    @dammitandy

    View Slide

  3. View Slide

  4. Making Android apps
    testable

    View Slide

  5. Dynamic languages
    Tests shouldn't drive
    implementation.

    View Slide

  6. Static languages
    You know nothing.

    View Slide

  7. Making Android apps testable
    1. Dependency injection
    2. Mocking & stubbing
    3. Unit & integration tests

    View Slide

  8. Dependency injection

    View Slide

  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.

    View Slide

  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;
    }
    }

    View Slide

  11. Dagger
    A Java dependency injection library

    View Slide

  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/

    View Slide

  13. Dagger Modules
    @Module
    public class MyModule {
    @Provides @Singleton
    public MyService provideMyService() {
    return new MyService();
    }
    }

    View Slide

  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();
    }
    }
    }

    View Slide

  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);
    }
    }

    View Slide

  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();
    }
    }

    View Slide

  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

    View Slide

  18. Mocking & Stubbing

    View Slide

  19. Mocking & Stubbing
    • Substitute runtime implementation for something that can
    be predictably tested in isolation
    • Verify behavior

    View Slide

  20. A Java mocking library

    View Slide

  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/

    View Slide

  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());

    View Slide

  23. Using Mockito
    // capture arguments
    ArgumentCaptor 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()));

    View Slide

  24. Unit & Integration
    Tests

    View Slide

  25. Tests or it didn't
    happen

    View Slide

  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());
    }
    }

    View Slide

  27. An Android UI testing library

    View Slide

  28. Espresso
    • Handles activity creation & state sync
    • Simple, concise API
    • Really fast!
    code.google.com/p/android-test-kit*

    View Slide

  29. UI testing with Espresso
    public class MyActivityTest extends ActivityInstrumentationTestCase2 {
    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))));
    }
    }

    View Slide

  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

    View Slide

  31. Testing the login activity
    • Simulate data entry and taps to validate UI functionality
    • Simulate an error without making a network request

    View Slide

  32. Testing the main activity
    • Verify the RecyclerView contains data
    • Simulate taps to validate the appropriate URLs are loaded in
    a WebView

    View Slide

  33. Testing the API
    • Use mock API response loaded from text file to validate
    JSON parsing

    View Slide

  34. Demo

    View Slide

  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.

    View Slide

  36. Additional resources
    • Bryan Stern/Circle Engineering - Instrumentation Testing
    with Dagger, Mockito, and Espresso
    engineering.circle.com/instrumentation-testing-with-dagger-
    mockito-and-espresso

    View Slide

  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.

    View Slide

  38. Questions?

    View Slide

  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

    View Slide