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 full-size slide

  2. Andy Dyer
    +AndrewDyer
    @dammitandy

    View full-size slide

  3. Making Android apps
    testable

    View full-size slide

  4. Dynamic languages
    Tests shouldn't drive
    implementation.

    View full-size slide

  5. Static languages
    You know nothing.

    View full-size slide

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

    View full-size slide

  7. Dependency injection

    View full-size slide

  8. 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 full-size slide

  9. 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 full-size slide

  10. Dagger
    A Java dependency injection library

    View full-size slide

  11. 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 full-size slide

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

    View full-size slide

  13. 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 full-size slide

  14. 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 full-size slide

  15. 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 full-size slide

  16. 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 full-size slide

  17. Mocking & Stubbing

    View full-size slide

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

    View full-size slide

  19. A Java mocking library

    View full-size slide

  20. 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 full-size slide

  21. 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 full-size slide

  22. 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 full-size slide

  23. Unit & Integration
    Tests

    View full-size slide

  24. Tests or it didn't
    happen

    View full-size slide

  25. 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 full-size slide

  26. An Android UI testing library

    View full-size slide

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

    View full-size slide

  28. 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 full-size slide

  29. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  33. 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 full-size slide

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

    View full-size slide

  35. 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 full-size slide

  36. 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 full-size slide