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

Testing your Android application at jDays 2015

Testing your Android application at jDays 2015

Slides from the 'Testing your Android app' session at jDays 2015 in Göteborg, Sweden

Filip Maelbrancke

March 18, 2015
Tweet

More Decks by Filip Maelbrancke

Other Decks in Programming

Transcript

  1. Test-Driven
    Android
    Development

    View Slide

  2. Android & Testing
    01
    02
    03
    Why?
    What?
    How?

    View Slide

  3. View Slide

  4. View Slide

  5. Your host
    Filip Maelbrancke
    Consultant @ AppFoundry
    [email protected]
    @fmaelbrancke

    View Slide

  6. View Slide

  7. Testing
    Ship with confidence

    View Slide

  8. TESTING
    Manual testing is tedious
    Humans are not very good at testing
    But computers are...

    View Slide

  9. “Computers are designed to do simple
    repetitive tasks. The second you have
    humans doing repetitive tasks, all the
    computers get together late at night
    and laugh at you...”
    Neil Ford

    View Slide

  10. Manual vs Automated

    View Slide

  11. Mitigate issues?
    Be pro-active…
    and test.

    View Slide

  12. EXCUSES
    We never
    make
    mistakes!
    The
    functionality is
    trivial
    Tests slow us
    down
    Management
    won’t let us
    WE = HERO TRIVIAL SLOW MANAGEMENT
    ǩ

    View Slide

  13. TEST TRAITS
    (UNIT)
    TESTS
    ISOLATED REPEATABLE FAST SELF-DOCUMENTING
    ž

    View Slide

  14. Repeatability
    (no matter what development stack he uses) in which they are being run
    Tests need to be run by every developer
    ♂ Tests must not rely on the environment

    View Slide

  15. Android dev

    View Slide

  16. d.android.com

    View Slide

  17. Instrumentation
    Process
    Test package InstrumentationTestRunner Application package
    Test case classes JUnit

    View Slide

  18. Instrumentation
    Test Activity state saving
    and restoration
    // Start the main activity of the application under test

    mActivity = getActivity();

    // Get a handle to the Activity object's main UI widget, a Spinner

    mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner);

    // Set the Spinner to a known position

    mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);

    // Stop the activity - The onDestroy() method should save the state of the Spinner

    mActivity.finish();

    // Re-start the Activity - the onResume() method should restore the state of the Spinner

    mActivity = getActivity();

    // Get the Spinner's current position

    int currentPosition = mActivity.getSpinnerPosition();

    // Assert that the current position is the same as the starting position

    assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);

    View Slide

  19. View Slide

  20. TEST TRAITS
    (UNIT)
    TESTS
    ISOLATED REPEATABLE FAST SELF-DOCUMENTING
    ž

    View Slide

  21. Self-documenting
    Testable code = clear Testable code = easy to follow
    we can just look at the test
    No need to explain how a component works No need to write documentation
    tests = usage examples

    View Slide

  22. Tests should also be …
    Easy to execute Easy to debug
    Realistic Fluent API

    View Slide

  23. View Slide

  24. TESTING
    ANNOUNCEMENT

    View Slide

  25. android.support.test.*
    for all Google testing frameworks
    Unbundled static testing library
    Ŷ
    Android support library
    Ŷ
    Full Gradle support
    Ŷ
    Open source / AOSP
    Ŷ
    Container
    Ŷ

    View Slide

  26. Testing support library
    AndroidJUnitRunner
    Ŷ
    Intento
    Ŷ UiAutomator
    Ŷ
    Espresso
    Ŷ

    View Slide

  27. JUnit 4 support
    @Parameters
    Test annotations
    Ŷ
    Test suite annotations
    Ŷ
    Parameterized tests
    Ŷ
    @Suite.SuiteClasses
    @Test
    @Before
    @After
    @BeforeClass
    @AfterClass

    View Slide

  28. Testing demo
    Simple master-detail app

    View Slide

  29. Espresso

    View Slide

  30. Espresso
    Easy API for test authors
    Find view
    Do stuff with view
    Check some state
    Without
    • waitUntil(timeout)
    • boilerplate

    View Slide

  31. Espresso API
    onView(Matcher)
    onData(Matcher)
    onView(Matcher)
    onView(Matcher)
    withId() / withText()
    is() / instanceOf()
    click() / enterText() / scrollTo()
    matches(Matcher)
    isDisplayed / isEnabled / hasFocus
    withText / …
    Find stuff Do stuff
    Check

    View Slide

  32. Espresso
    Espresso
    onView(withId(R.id.some_id)).perform(click());


    onView(withText("Hi there”)).check(matches(isDisplayed()));


    onView(withId(R.id.some_id)).check(matches(withText(containsString(“someString”))));


    onData(is(someObject)).perform(click());

    View Slide

  33. Espresso API
    onView(Matcher)
    onData(Matcher)
    Espresso
    withId()
    withText()
    ViewMatchers
    perform()
    check()
    ViewInteraction / DataInteraction
    click()
    enterText()
    scrollTo()
    ViewActions
    matches(Matcher)
    doesNotExist()
    ViewAssertions
    find view to operate on
    composed of
    © Stephan Linzner (Google)

    View Slide

  34. View Matchers
    Espresso uses Hamcrest Matchers
    Espresso Matchers = Hamcrest
    Ŷ
    Make assertions on views
    Ŷ
    Filter views
    Ŷ
    Reuse existing Matchers
    Ŷ Create your own Matchers
    Ŷ
    from hamcrest-core and hamcrest-library

    View Slide

  35. View Matchers
    provides Android specific View Matchers
    ViewMatchers
    Ŷ
    • click()
    • scrollTo()
    • writeText()
    Basic view actions
    Ŷ

    View Slide

  36. Expressiveness

    View Slide

  37. Hamcrest
    assertThat(audience, is(payingAttention()))
    Verification
    Mocking
    Assertions

    View Slide

  38. Hamcrest
    Library of matchers
    Ŷ Syntactic sugar
    Ŷ
    Assert
    assertThat(someString, is(equalTo(“Expected”)));
    actual value expectation on the value (Matcher)

    View Slide

  39. Hamcrest
    Assert
    Hamcrest
    assertEquals(“Conversion failed”, “Expected”, someString);
    assertThat(someString, is(equalTo(“Expected”)));

    View Slide

  40. Hamcrest: collections
    Assert
    assertEquals(person, bigbangCharacters.getPersons().iterator().next());
    Hamcrest
    assertThat(bigbangCharacters.getPersons(), hasItem(person));

    View Slide

  41. Hamcrest matchers
    Core
    anything

    is


    Logical
    allOf

    anyOf

    not
    Object
    equalTo

    instanceOf

    (not)nullValue
    Custom Beans
    hasProperty
    Text
    equalToIgnoringCase

    startsWith


    Number
    greaterThan(OrEqualTo)

    closeTo
    Collections
    hasItem, hasItems

    hasItemInArray

    View Slide

  42. View Matchers
    Hamcrest
    Ŷ allOf() / anyOf() ➪ give more power
    Ŷ
    TestRunner
    // assert that textview does not start with with "XYZ" and contains "ABC" anywhere

    onView(withId(R.id.celsius_textview))

    .check(matches(allOf(withText(not(startsWith("XYZ"))), withText(containsString("ABC")))));

    // assert that textview ends with "XYZ" or contains "ABC" anywhere

    onView(withId(R.id.celsius_textview))

    .check(matches(anyOf(withText(endsWith("XYZ")), withText(containsString("ABC")))));

    View Slide

  43. Hamcrest
    Lots of useful matchers
    Ŷ Custom matchers
    Ŷ
    not a number
    public void testSquareRootOfMinusOneIsNotANumber() {

    assertThat(Math.sqrt(-1), is(notANumber()));

    }

    View Slide

  44. Hamcrest custom matcher
    Hamcrest matcher
    /**

    * Hamcrest matcher: test if a double has the value NaN (not a number)

    */

    public class IsNotANumber extends TypeSafeMatcher {


    @Override

    public boolean matchesSafely(Double number) {

    return number.isNaN();

    }


    public void describeTo(Description description) {

    description.appendText("not a number");

    }


    @Factory

    public static Matcher notANumber() {

    return new IsNotANumber();

    }

    }

    View Slide

  45. Testing demo
    Testing a temperature converter

    View Slide

  46. Custom ViewAction
    Espresso ViewAction
    /**

    * Clears view text by setting {@link EditText}s text property to "".

    */

    public class ClearTextAction implements ViewAction {


    @Override

    public Matcher getConstraints() {

    return allOf(isDisplayed(), isAssignableFrom(EditText.class));

    }


    @Override

    public void perform(UiController uiController, View view) {

    ((EditText) view).setText("");

    }


    @Override

    public String getDescription() {

    return "Clear the EditText component";

    }

    }

    View Slide

  47. Custom ViewAction
    Espresso ViewAction
    /**

    * Your own custom View Actions.

    */

    public class CustomViewActions {


    private CustomViewActions() {

    }


    /**

    * Returns an action that clears text on the view. Extra thoroughly!

    */

    public static ViewAction clearTextExtraThoroughly() {

    return new ClearTextAction();

    }

    }

    View Slide

  48. Use Custom ViewAction
    Espresso ViewAction
    public void testClearCelsiusTextField() {

    onView(withText("Celsius")).perform(clearTextExtraThoroughly());

    }

    View Slide

  49. Reliability through synchronization
    between Instrumentation thread and UI thread
    for background resources
    Espresso synchronizes AsyncTasks default thread pool
    • wait until app is idle
    • perform operation on the UI thread
    • wait until completion
    • check result
    for custom implementations

    IntentService

    HandlerThread

    ThreadPool
    Synchronization
    Ŷ Synchronization
    Ŷ
    Synchronization
    Ŷ
    AsyncTask
    Ŷ IdlingResource interface
    Ŷ

    View Slide

  50. Espresso synchronization
    Espresso IdlingResource
    public class IdleMonitor implements IdlingResource {


    @Override

    public String getName() {

    return IdleMonitor.class.getSimpleName();

    }


    @Override

    public boolean isIdleNow() {

    // return true if resource is idle

    return false;

    }


    @Override

    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {

    // store a reference to the resourceCallback

    // notify resourceCallback when idle

    }

    }
    Espresso.registerIdlingResources(idlingResource);

    View Slide

  51. TEST TRAITS
    (UNIT)
    TESTS
    ISOLATED REPEATABLE FAST SELF-DOCUMENTING
    ž

    View Slide

  52. Speed
    The shorter, the less distraction less of an interruption to our workflow

    View Slide

  53. Speed
    The shorter, the less distraction less of an interruption to our workflow

    View Slide

  54. Android testing
    AndroidTestCase needs an instance of the emulator to run
    • spin up emulator
    • deploy the APK
    • run the actual tests
    Big time sink:
    Ŷ

    View Slide

  55. Android testing
    Android core libraries depend upon the actual Android operating system

    View Slide

  56. Robolectric

    View Slide

  57. Robolectric
    that would otherwise need an emulator / device
    (mock implementations of Android core libraries)
    too slow / hard for unit testing
    without the baggage of a device
    Replaces the behavior of code
    Ŷ Write JUnit tests
    Ŷ
    When?
    Ŷ
    Does this by using Shadow classes
    Ŷ
    InstrumentationTestCase
    Ŷ vs real device / speedy emulator
    Ŷ
    http://robolectric.org/

    View Slide

  58. Robolectric
    Annotate your class
    @RunWith(RobolectricTestRunner.class)

    public class MyActivityTest {


    @Test

    public void clickingButton_shouldChangeResultsViewText() throws Exception {

    MyActivity activity = Robolectric.buildActivity(MyActivity.class).create().get();


    Button pressMeButton = (Button) activity.findViewById(R.id.press_me_button);

    TextView results = (TextView) activity.findViewById(R.id.results_text_view);


    pressMeButton.performClick();

    String resultsText = results.getText().toString();

    assertThat(resultsText, equalTo("Testing Android Rocks!"));

    }

    View Slide

  59. Robolectric setup
    • Robolectric
    • Novoda gradle-android-test-plugin
    see Paul Blundell
    Tests run inside the JVM
    Ŷ Gradle - Maven
    Ŷ
    Configure it yourself
    Ŷ
    Gradle plugin
    Ŷ

    View Slide

  60. Android testing JUnit 4 Support
    JUnit 4 test
    @RunWith(AndroidJUnit4.class)

    public class AndroidJUnit4Test {


    Conference jDays;


    @Before

    public void setUp() {

    jDays = Conferences.newInstance(Conference.JDAYS);

    jDays.start();

    }


    @After

    public void tearDown() {

    jDays.stop();

    }


    @Test

    public void testPreconditions() {

    assertThat(jDays, is(notNullValue()));

    }


    @Test

    public void jDaysSouldBeGreat() {

    assertThat(jDays, is(awesome()));

    }

    View Slide

  61. Testing support
    JUnit 4
    Ŷ
    Mockable android.jar
    Ŷ First-party unit test support
    Ŷ
    Espresso 2
    Ŷ

    View Slide

  62. Mocking &
    Stubbing

    View Slide

  63. Mockito
    in isolation
    Change runtime implementation
    Ŷ That can be predictably tested
    Ŷ
    Verify behaviour
    Ŷ
    Mockito
    Ŷ

    View Slide

  64. Mockito
    the behaviour of your components during a test
    Java mocking framework
    Ŷ Mock dependencies
    Ŷ
    Inject Mocks to validate
    Ŷ
    Mock function return values
    Ŷ
    https://code.google.com/p/mockito/

    View Slide

  65. Mockito
    Mockito
    @Test

    public void testMockedConverter() throws Exception {

    // expectations

    final float expected_result = 32.0f;


    // given

    ConverterActivity activity = Robolectric.buildActivity(ConverterActivity.class).create().get();

    activity.onCreate(new Bundle());

    EditText celsiusField = (EditText) activity.findViewById(R.id.celsius_textview);

    celsiusField.setText("0");


    CelsiusFahrenheitConverter mockConverter = Mockito.mock(CelsiusFahrenheitConverter.class);

    Mockito.when(mockConverter.convertCelsiusToFahrenheit(Mockito.anyFloat())).thenReturn(expected_result);

    activity.setConverter(mockConverter);


    // when

    Button convertButton = (Button) activity.findViewById(R.id.conversion_button);

    convertButton.performClick();


    // then

    Mockito.verify(mockConverter, Mockito.times(1)).convertCelsiusToFahrenheit(Mockito.anyFloat());

    TextView fahrenheitField = (TextView) activity.findViewById(R.id.fahrenheit_textview);

    String fahrenheitString = fahrenheitField.getText().toString();

    assertThat(fahrenheitString, equalTo(String.valueOf(expected_result)));

    }

    View Slide

  66. The Monkey

    View Slide

  67. evil monkey…

    View Slide

  68. Monkey
    pseudo-random stream of user events
    Generates events
    Ŷ clicks
    Ŷ
    touches
    Ŷ gestures
    Ŷ
    system-level events
    Ŷ Stress test your app
    Ŷ

    View Slide

  69. Monkey
    number of events to attempt
    operational constraints, such as restricting the test to a
    single package
    Event types and frequencies
    command-line tool
    Ŷ basic configuration
    Ŷ
    $ adb shell monkey -p your.package.name -v 500
    Ŷ

    View Slide

  70. AssertJ Android
    for checking assertions to read / write
    http://square.github.io/assertj-android/
    Syntactic sugar
    Ŷ Extension of the AssertJ library
    Ŷ
    Makes tests easier
    Ŷ
    Fluent syntax
    Ŷ

    View Slide

  71. AssertJ Android
    Assertions for Android objects
    Ŷ + support libraries
    Ŷ

    View Slide

  72. AssertJ Android
    Regular JUnit
    AssertJ Android
    assertEquals(View.GONE, view.getVisibility());
    assertThat(view).isGone();
    expected: <8> but was: <4>
    Expected visibility but was .

    View Slide

  73. AssertJ Android
    Regular JUnit
    assertEquals(View.VISIBLE, layout.getVisibility());
    assertEquals(VERTICAL, layout.getOrientation());
    assertEquals(4, layout.getChildCount());
    assertEquals(SHOW_DIVIDERS_MIDDLE, layout.getShowDividers());
    AssertJ Android
    assertThat(layout).isVisible()
    .isVertical()
    .hasChildCount(4)
    .hasShowDividers(SHOW_DIVIDERS_MIDDLE);

    View Slide

  74. Testing
    network
    resources

    View Slide

  75. Testing network resources
    Unit tests
    Ŷ Integration tests
    Ŷ
    Retrofit
    Ŷ

    View Slide

  76. Testing network resources
    how to trigger edge cases like API rate limit exceeded? HTTP calls should be asynchronous

    Hit a real server?
    Ŷ Brittle tests!
    Ŷ
    Slow tests
    Ŷ
    Network or server is down
    Ŷ
    Incomplete tests
    Ŷ Further complicated in Android
    Ŷ

    View Slide

  77. Testing network resources
    Use a test server
    Ŷ Mock the Retrofit interface
    Ŷ

    View Slide

  78. Using a test server
    • virtually no changes or code required in your app
    codebase
    • can be shared across multiple platforms (Android, iOS,
    web, …)
    • another dependency (that can fail)
    • something new to figure out
    • not always easy to trigger errors or edge cases
    • what if ‘state’ is needed?
    • ‘slow’ tests execution, still HTTP calls needed
    PROs
    Ŷ CONs
    Ŷ

    View Slide

  79. Mocking the Retrofit interface
    • robust: no network errors, timeouts, …
    • easy to generate errors / edge cases
    • easy to control server-side state
    • write your dummy data in Java
    • extra test code needed
    • you don’t test the JSON deserialization (but you can add
    unit tests)
    • mock a part of your application ➪ not a true integration
    test
    PROs
    Ŷ CONs
    Ŷ
    You trust the test suites of Retrofit, Gson, … and mock out the data

    View Slide

  80. Network testing
    Mock network interface
    ‘Fake’-‘real’ server

    View Slide

  81. Mocking the Retrofit interface
    MockRestAdapter sample

    View Slide

  82. Mocking the Retrofit interface
    Retrofit mock
    public interface GitHub {

    @GET("/repos/{owner}/{repo}/contributors")

    List contributors(@Path("owner") String owner, @Path("repo") String repo);

    }

    View Slide

  83. Mocking the Retrofit interface
    Canned data
    /** A mock implementation of the {@link GitHub} API interface. */

    static class MockGitHub implements GitHub {

    private final Map>> ownerRepoContributors;


    public MockGitHub() {

    ownerRepoContributors = new LinkedHashMap>>();


    // Seed some mock data.

    addContributor("square", "retrofit", "John Doe", 12);

    addContributor("square", "retrofit", "Bob Smith", 2);

    addContributor("square", "retrofit", "Big Bird", 40);

    addContributor("square", "picasso", "Proposition Joe", 39);

    addContributor("square", "picasso", "Keiser Soze", 152);

    }

    View Slide

  84. Mocking the Retrofit interface
    Mock
    @Override
    public List contributors(String owner, String repo) {

    Map> repoContributors = ownerRepoContributors.get(owner);

    if (repoContributors == null) {

    return Collections.emptyList();

    }

    List contributors = repoContributors.get(repo);

    if (contributors == null) {

    return Collections.emptyList();

    }

    return contributors;

    }

    View Slide

  85. Mocking the Retrofit interface
    Mock
    // Create a very simple REST adapter which points to the GitHub API endpoint.

    RestAdapter restAdapter = new RestAdapter.Builder()

    .setEndpoint(API_URL)

    .build();


    // Wrap our REST adapter to allow mock implementations and fake network delay.

    MockRestAdapter mockRestAdapter = MockRestAdapter.from(restAdapter);


    // Instantiate a mock object so we can interact with it later.

    MockGitHub mockGitHub = new MockGitHub();

    // Use the mock REST adapter and our mock object to create the API interface.

    GitHub gitHub = mockRestAdapter.create(GitHub.class, mockGitHub);


    // Query for some contributors for a few repositories.

    printContributors(gitHub, "square", "retrofit");

    printContributors(gitHub, "square", "picasso");

    View Slide

  86. Network testing
    Mock network interface
    ‘Fake’-‘real’ server

    View Slide

  87. MockWebServer
    predictable requests-responses
    Scriptable web server
    Ŷ okhttp (square)
    Ŷ
    Specify responses
    Ŷ
    Test HTTP clients
    Ŷ
    Verify requests
    Ŷ Like Mockito (for HTTP stack)
    Ŷ
    • script your mocks
    • run app code
    • verify that expected requests were made

    View Slide

  88. MockWebServer
    MockWebServer
    // Create a MockWebServer. These are lean enough that you can create
    // a new instance for every unit test.
    MockWebServer mockWebServer = new MockWebServer();

    mockWebServer.play();


    // Setup the MockWebServer & schedule a response

    MockResponse mockResponse = new MockResponse();

    mockResponse.setResponseCode(500);

    mockWebServer().enqueue(mockResponse);


    // Setup your networking lib. Ask the server for its url.

    … mockWebServer.getUrl(“/“);


    DO YOUR TEST

    View Slide

  89. MockWebServer
    Java test
    @Test
    public void exampleTest() {
    // make your app do requests to http://localhost:8080
    // setup MockWebServer
    onView(withId(R.id.submit_button)).perform(click());
    onView(withText(“error message”)).check(matches(isDisplayed()));
    verify(postRequestedFor(urlMatching(“/my/resource/[a-z0-9]+”)));
    }

    View Slide

  90. Android testing tools

    View Slide

  91. Application
    ARCHITECTURE

    View Slide

  92. View Slide

  93. Software design patterns
    Good code = testable code
    Ŷ Dependency injection
    Ŷ

    View Slide

  94. Software design patterns
    Architecting Android Applications with Dagger
    Dependency injection
    Ŷ Dagger
    Ŷ
    Avoid reflection at runtime
    Ŷ
    Define dependencies
    Ŷ
    Jake Wharton
    Ŷ
    https://parleys.com/play/529bde2ce4b0e619540cc3ae

    View Slide

  95. Dependency injection
    Decoupling
    Ŷ Swap mocks in for tests
    Ŷ

    View Slide

  96. Android lifecycle

    View Slide

  97. Model View Presenter
    Model
    Presenter
    View

    View Slide

  98. Model View Presenter
    Model
    Presenter
    View
    Android
    UI

    View Slide

  99. Model View Presenter
    Testable presentation logic
    Ŷ No Android dependencies
    Ŷ

    View Slide

  100. AUTOMATION &

    REPORTING

    View Slide

  101. Automated distributed testing
    Automated distributed instrumentation testing
    Screenshots
    share with the designers…?
    in parallel
    Run tests on multiple devices
    Reports / logs captured for each test

    View Slide

  102. Spoon
    https://square.github.io/spoon/
    Automate test execution
    Ŷ across multiple devices
    Ŷ
    Aggregation of screenshots
    Ŷ
    Aggregate the results
    Ŷ

    View Slide

  103. Spoon
    High level tests overview
    Ŷ Test failure specific
    Ŷ
    to a single device or all devices

    View Slide

  104. Spoon: device view
    Results of each test on one device
    Ŷ Useful for tracking down
    Ŷ
    device-specific failures of individual tests

    View Slide

  105. Spoon: test view
    Results of single test on all devices
    Ŷ

    View Slide

  106. Spoon: screenshots
    Visual inspection of test execution
    Ŷ
    screenshots
    Spoon.screenshot(activity, "initial_state");
    /* Normal test code... */
    Spoon.screenshot(activity, "after_login");
    http://square.github.io/spoon/sample/

    View Slide

  107. Build pipeline
    Checkout /
    compile
    Unit tests
    Test
    coverage
    Code
    analysis
    Create
    deployable
    artifact
    Deploy for
    automatic
    QA test
    Trigger
    automated
    QA stage

    View Slide

  108. Build pipeline tools
    Build (maven -
    gradle)
    Dependency repo
    (nexus - artifactory)
    Testing framework
    (JUnit - ...)
    Test coverage
    (Cobertura - Emma
    - Jacoco)
    Code analysis
    (Checkstyle,
    findbugs, pmd,
    Android Lint)
    Creation of
    deployable artifact
    (buildtool, artifact
    repo)
    Trigger
    next
    stage

    View Slide

  109. Continuous integration benefits
    without additional effort
    Fast feedback - fewer errors
    Ŷ Test everything on every (nightly) build
    Ŷ
    Regression tests
    Ŷ
    Less manual testing
    Ŷ

    View Slide

  110. Unit tests are real measure of project
    health and code quality

    View Slide

  111. Jenkins

    View Slide

  112. Continuous integration
    • Jenkins
    • Bamboo
    Travis

    Server
    Ŷ Hosted
    Ŷ
    https://travis-ci.org

    View Slide

  113. Test coverage
    open-source toolkit for measuring and reporting Java code
    coverage
    Metrics
    Ŷ Code safely
    Ŷ
    JaCoCo
    Ŷ
    Find risky code
    Ŷ

    View Slide

  114. Android test coverage

    View Slide

  115. Coverage
    88%

    View Slide

  116. References

    View Slide

  117. Suggested reading
    Android Application Testing Guide
    Diego Torres Milano (9781849513500)
    Robotium Automated Testing for Android
    Hrushikesh Zadgaonkar (9781782168010)
    The Busy Coder’s Guide to Android Development
    Mark Murphy - http://commonsware.com/Android/

    View Slide

  118. Suggested reading
    Test Driven Development: By Example
    Beck, Kent (978-0321146533)
    Continuous Integration: Improving Software Quality and
    Reducing Risk
    Duvall, Paul M. et al. (978-0321336385)
    Working Effectively with Legacy Code
    Feathers, Michael (978-0131177055)

    View Slide

  119. Questions?
    Filip Maelbrancke
    Consultant @ AppFoundry
    [email protected]
    @fmaelbrancke

    View Slide