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

Getting Started with AndroidX Test

Getting Started with AndroidX Test

Testing on Android is slowly becoming more approachable. With AndroidX Test, you can use a single test API to run your instrumentation tests either on your mobile device, or on your computer using Robolectric. This is great, so how do you start? In this talk you’ll learn how to use AndroidX Test and Espresso to get started writing Integration tests for your app.

Victoria Gonda

April 23, 2019
Tweet

More Decks by Victoria Gonda

Other Decks in Programming

Transcript

  1. @TTGonda
    victoriagonda.com
    Getting Started With
    AndroidX Test
    Victoria Gonda

    View Slide

  2. @TTGonda
    victoriagonda.com
    Why Test?
    • Make changes with confidence
    • Living documentation
    • Encourages maintainable, clean code

    View Slide

  3. @TTGonda
    victoriagonda.com
    https://twitter.com/TTGonda/status/1100931379150360576

    View Slide

  4. @TTGonda
    victoriagonda.com
    https://twitter.com/TTGonda/status/1101298440552759297

    View Slide

  5. @TTGonda
    victoriagonda.com
    https://twitter.com/TTGonda/status/1101677434162814976

    View Slide

  6. @TTGonda
    victoriagonda.com
    https://twitter.com/TTGonda/status/1102010808324767744

    View Slide

  7. @TTGonda
    victoriagonda.com
    https://twitter.com/saraceni_br/status/1102643475793829888

    View Slide

  8. @TTGonda
    victoriagonda.com
    Victoria Gonda
    • Android Engineer at Buffer
    • Author and Editor at
    RayWenderlich.com
    • Chicago native
    • Hedgehog and cat owner
    • she/her

    View Slide

  9. @TTGonda
    victoriagonda.com
    Testing Tools
    • JUnit4
    • JUnit5
    • Retrofit-mock
    • Espresso
    • Mockito
    • Robolectric
    • Spek
    • KotlinTest
    • MockK
    • Kakao
    • Hiroaki
    • Barista
    • Biscotti
    • AndroidX Test

    View Slide

  10. @TTGonda
    victoriagonda.com
    AndroidX Test

    View Slide

  11. @TTGonda
    victoriagonda.com
    AndroidX Test
    • Includes some of the libraries you already know, plus some
    new ones
    • Aims to solve the issue of too many libraries
    • Can be run both on device or on the JVM

    View Slide

  12. @TTGonda
    victoriagonda.com
    What’s Inside?

    View Slide

  13. @TTGonda
    victoriagonda.com
    What’s Inside?
    • JUnit
    • Core
    • Espresso
    • Truth Assertions

    View Slide

  14. @TTGonda
    victoriagonda.com
    JUnit
    Core
    Espresso
    Truth

    View Slide

  15. @TTGonda
    victoriagonda.com

    View Slide

  16. @TTGonda
    victoriagonda.com
    JUnit
    Core
    Espresso
    Truth

    View Slide

  17. @TTGonda
    victoriagonda.com
    JUnit

    View Slide

  18. @TTGonda
    victoriagonda.com
    defaultConfig {
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    testImplementation ‘junit:junit:4.12'
    testImplementation 'androidx.test.ext:junit:1.1.0'

    View Slide

  19. @TTGonda
    victoriagonda.com
    @RunWith(AndroidJUnit4::class)
    class MainActivityTest

    View Slide

  20. @TTGonda
    victoriagonda.com
    val context =
    ApplicationProvider.getApplicationContext()
    val appName = context.getString(R.string.app_name)

    View Slide

  21. @TTGonda
    victoriagonda.com
    android {
    testOptions.unitTests.includeAndroidResources = true
    }
    testImplementation 'org.robolectric:robolectric:4.2.1'

    View Slide

  22. @TTGonda
    victoriagonda.com
    JUnit
    Core
    Espresso
    Truth

    View Slide

  23. @TTGonda
    victoriagonda.com
    Core

    View Slide

  24. @TTGonda
    victoriagonda.com
    @Test
    fun mainActivityLaunches() {
    ActivityScenario.launch(MainActivity::class.java)
    }

    View Slide

  25. @TTGonda
    victoriagonda.com
    val scenario = ActivityScenario
    .launch(MainActivity::class.java)
    scenario.moveToState(Lifecycle.State.CREATED)
    scenario.moveToState(Lifecycle.State.RESUMED)
    scenario.recreate()
    scenario.close()

    View Slide

  26. @TTGonda
    victoriagonda.com
    val scenario = ActivityScenario
    .launch(MainActivity::class.java)
    scenario.moveToState(Lifecycle.State.CREATED)
    scenario.moveToState(Lifecycle.State.RESUMED)
    scenario.recreate()
    scenario.close()

    View Slide

  27. @TTGonda
    victoriagonda.com
    val scenario = ActivityScenario
    .launch(MainActivity::class.java)
    scenario.moveToState(Lifecycle.State.CREATED)
    scenario.moveToState(Lifecycle.State.RESUMED)
    scenario.recreate()
    scenario.close()

    View Slide

  28. @TTGonda
    victoriagonda.com
    val scenario = ActivityScenario
    .launch(MainActivity::class.java)
    scenario.moveToState(Lifecycle.State.CREATED)
    scenario.moveToState(Lifecycle.State.RESUMED)
    scenario.recreate()
    scenario.close()

    View Slide

  29. @TTGonda
    victoriagonda.com
    @get:Rule
    val activityRule =
    ActivityScenarioRule(MainActivity::class.java)
    // in test function
    val scenario = activityRule.scenario

    View Slide

  30. @TTGonda
    victoriagonda.com
    @get:Rule
    val activityRule =
    ActivityScenarioRule(MainActivity::class.java)
    // in test function
    val scenario = activityRule.scenario

    View Slide

  31. @TTGonda
    victoriagonda.com
    testImplementation 'androidx.fragment:fragment-testing:1.1.0-alpha05'

    View Slide

  32. @TTGonda
    victoriagonda.com
    val fragmentScenario =
    FragmentScenario.launch(TaskFragment::class.java)
    fragmentScenario.moveToState(Lifecycle.State.RESUMED)

    View Slide

  33. @TTGonda
    victoriagonda.com
    val applicationInfo = ApplicationInfoBuilder
    .newBuilder()
    .setPackageName("com.victoriagonda.tasklist")
    .build()
    PackageInfoBuilder.newBuilder()
    .setApplicationInfo(applicationInfo)
    .build()

    View Slide

  34. @TTGonda
    victoriagonda.com
    JUnit
    Core
    Espresso
    Truth

    View Slide

  35. @TTGonda
    victoriagonda.com
    Espresso

    View Slide

  36. @TTGonda
    victoriagonda.com
    testImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    View Slide

  37. @TTGonda
    victoriagonda.com
    ViewMatchers.withId
    ViewActions.click
    ViewAssertions.matches

    View Slide

  38. @TTGonda
    victoriagonda.com
    @Test
    fun mainActivityShowsAddButton() {
    val scenario = ActivityScenario
    .launch(MainActivity::class.java)
    onView(withId(R.id.buttonAddList))
    .check(matches(isDisplayed()))
    }

    View Slide

  39. @TTGonda
    victoriagonda.com
    @Test
    fun mainActivityShowsAddButton() {
    val scenario = ActivityScenario
    .launch(MainActivity::class.java)
    onView(withId(R.id.buttonAddList))
    .check(matches(isDisplayed()))
    }

    View Slide

  40. @TTGonda
    victoriagonda.com
    testImplementation 'androidx.test.espresso:espresso-intents:3.1.1'
    testImplementation 'androidx.test.espresso:espresso-contrib:3.1.1'

    View Slide

  41. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    Intents.intended(IntentMatchers
    .hasComponent(DetailActivity::class.java.name))
    Intents.release()
    }

    View Slide

  42. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    Intents.intended(IntentMatchers
    .hasComponent(DetailActivity::class.java.name))
    Intents.release()
    }

    View Slide

  43. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    Intents.intended(IntentMatchers
    .hasComponent(DetailActivity::class.java.name))
    Intents.release()
    }

    View Slide

  44. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    Intents.intended(IntentMatchers
    .hasComponent(DetailActivity::class.java.name))
    Intents.release()
    }

    View Slide

  45. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    Intents.intended(IntentMatchers
    .hasComponent(DetailActivity::class.java.name))
    Intents.release()
    }

    View Slide

  46. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    Intents.intended(IntentMatchers
    .hasComponent(DetailActivity::class.java.name))
    Intents.release()
    }

    View Slide

  47. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    Intents.intended(IntentMatchers
    .hasComponent(DetailActivity::class.java.name))
    Intents.release()
    }

    View Slide

  48. @TTGonda
    victoriagonda.com
    JUnit
    Core
    Espresso
    Truth

    View Slide

  49. @TTGonda
    victoriagonda.com
    Truth

    View Slide

  50. @TTGonda
    victoriagonda.com
    testImplementation 'androidx.test.ext:truth:1.1.0'

    View Slide

  51. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    val intent = getIntents().first()
    IntentSubject.assertThat(intent)
    .hasComponentClass(DetailActivity::class.java)
    Intents.release()
    }

    View Slide

  52. @TTGonda
    victoriagonda.com
    @Test
    fun selectingListOpensDetailActivity() {
    ActivityScenario.launch(MainActivity::class.java)
    Intents.init()
    onView(withId(R.id.recyclerView))
    .perform(RecyclerViewActions
    .actionOnItemAtPosition(0, click()))
    val intent = getIntents().first()
    IntentSubject.assertThat(intent)
    .hasComponentClass(DetailActivity::class.java)
    Intents.release()
    }

    View Slide

  53. @TTGonda
    victoriagonda.com
    val intent = DetailActivity.newIntent(tasklist,
    ApplicationProvider.getApplicationContext())
    val bundleSubject: BundleSubject =
    assertThat(intent).extras()
    bundleSubject.containsKey("EXTRA_TASKLIST")
    val integerSubject: IntegerSubject =
    bundleSubject.integer("EXTRA_TASKLIST")
    integerSubject.isEqualTo(tasklist.id)

    View Slide

  54. @TTGonda
    victoriagonda.com
    fun newIntent(list: Tasklist, context: Context): Intent {
    return Intent(context, DetailActivity::class.java)
    .apply {
    putExtra("EXTRA_TASKLIST", list.id)
    }
    }

    View Slide

  55. @TTGonda
    victoriagonda.com
    val intent = DetailActivity.newIntent(tasklist,
    ApplicationProvider.getApplicationContext())
    val bundleSubject: BundleSubject =
    assertThat(intent).extras()
    bundleSubject.containsKey("EXTRA_TASKLIST")
    val integerSubject: IntegerSubject =
    bundleSubject.integer("EXTRA_TASKLIST")
    integerSubject.isEqualTo(tasklist.id)

    View Slide

  56. @TTGonda
    victoriagonda.com
    val intent = DetailActivity.newIntent(tasklist,
    ApplicationProvider.getApplicationContext())
    val bundleSubject: BundleSubject =
    assertThat(intent).extras()
    bundleSubject.containsKey("EXTRA_TASKLIST")
    val integerSubject: IntegerSubject =
    bundleSubject.integer("EXTRA_TASKLIST")
    integerSubject.isEqualTo(tasklist.id)

    View Slide

  57. @TTGonda
    victoriagonda.com
    val intent = DetailActivity.newIntent(tasklist,
    ApplicationProvider.getApplicationContext())
    val bundleSubject: BundleSubject =
    assertThat(intent).extras()
    bundleSubject.containsKey("EXTRA_TASKLIST")
    val integerSubject: IntegerSubject =
    bundleSubject.integer("EXTRA_TASKLIST")
    integerSubject.isEqualTo(tasklist.id)

    View Slide

  58. @TTGonda
    victoriagonda.com
    value of : intent.getComponent().getClassName()
    expected : com.victoriagonda.android.tasklist.DetailActivity
    but was : com.victoriagonda.android.tasklist.MainActivity

    View Slide

  59. @TTGonda
    victoriagonda.com
    Wanted to match 1 intents. Actually matched 0 intents.
    IntentMatcher: has component: has component with: class name: is
    "com.victoriagonda.android.tasklist.DetailActivity" package name:
    an instance of java.lang.String short class name: an instance of
    java.lang.String
    Matched intents:[]
    Recorded intents:
    -Intent { cmp=com.victoriagonda.android.tasklist/.MainActivity (has
    extras) } handling packages:[[com.victoriagonda.android.tasklist]],
    extras:[Bundle[{EXTRA_TASKLIST=1}]])

    View Slide

  60. @TTGonda
    victoriagonda.com
    value of : intent.getExtras()
    expected to contain key EXTRA_TASKLIST
    but was : Bundle[{EXTRA_TASKNAME=Laundry}]

    View Slide

  61. @TTGonda
    victoriagonda.com
    value of : intent.getExtras().getInt(EXTRA_TASKLIST)
    expected : 4
    but was : 5

    View Slide

  62. @TTGonda
    victoriagonda.com
    junit.framework.AssertionFailedError:
    Expected :4
    Actual :5

    View Slide

  63. @TTGonda
    victoriagonda.com
    • MotionEvent
    • PointerCoords
    • PointerProperties
    • IntentCorrespondences
    • IntentCorrespondences
    • NotificationAction
    • Notification
    • PendingIntent

    View Slide

  64. @TTGonda
    victoriagonda.com
    Wrapping Up
    • Google wrapped up all testing under one AndroidX roof
    • Four parts, JUnit, Core, Espresso, Truth
    • Can ALL be run on or off device
    • Hopefully means less APIs to learn and less worry about on or
    off device

    View Slide

  65. @TTGonda
    victoriagonda.com
    Project Nitrogin?

    View Slide

  66. @TTGonda
    victoriagonda.com
    Thanks!
    https://vgonda.github.io/Talk-Resources/

    View Slide