$30 off During Our Annual Pro Sale. View Details »

Build Testble Apps for Androidの紹介 / introduce-build-testable-apps-for-android

Build Testble Apps for Androidの紹介 / introduce-build-testable-apps-for-android

DeNA.apk#1 【Google I/O 報告会】の発表資料です
https://dena.connpass.com/event/128184/

tkmnzm

May 16, 2019
Tweet

More Decks by tkmnzm

Other Decks in Programming

Transcript

  1. Build Testable Apps
    for Androidͷ঺հ
    SWETάϧʔϓ Nozomi Takuma

    View Slide

  2. ࣗݾ঺հ
    Nozomi Takuma
    DeNAͷSWETάϧʔϓ
    Androidͱςετ͕޷͖

    View Slide

  3. Session Summary
    AndroidΞϓϦʹ͓͚Δࣗಈςετͷ
    ઓུ
    TestableͳΞϓϦͷߏ੒
    Unit/Integra=on/E2Eςετͷ࣮૷

    View Slide

  4. ࣗಈςετͷઓུ
    Scope/Speed/Fidelityͷόϥϯε
    ςετϐϥϛου

    View Slide

  5. ࣗಈςετͷઓུ

    View Slide

  6. TODOΞϓϦΛྫʹ
    Cri=cal User Journey
    λεΫΛ࡞੒͢Δ
    λεΫͷਐḿΛ֬ೝ͢Δ

    View Slide

  7. Monolithic Architecture

    View Slide

  8. Ϋϥε͝ͱΛ੾Γ཭͢ͷ
    ͕೉͍͠
    είʔϓͷখ͍͞ςετ
    ͕Ͱ͖ͣɺE2Eςετ͕

    ૿͍͑ͯ͘
    Monolithic Architecture

    View Slide

  9. Layered Architecture

    View Slide

  10. Unitςετ͸໰୊ͳ͠
    ࠷ऴతʹ֤Layer͕Monoli=cʹͳΓ

    ΠϯςάϨʔγϣϯςετΑΓ΋

    E2Eςετ͕૿͍͑ͯ͘
    Layered Architecture

    View Slide

  11. Modular Architecture

    View Slide

  12. Architecture Component
    Naviga=on
    Data Binding
    Live Data
    Room
    View
    ViewModel
    Model

    View Slide

  13. શମਤ

    View Slide

  14. TDD(TDD&ATDDͬΆ͍?)

    View Slide

  15. TDD(TDD&ATDDͬΆ͍?)
    ͱ͍͏લஔ͖͕͋Γ

    E2Eςετͷઆ໌͔Β͸͡·Γ·͢

    View Slide

  16. E2E Test

    View Slide

  17. Happy PathΛ

    ςετ
    E2E Test Scope

    View Slide

  18. E2E Test Code
    @Test
    fun createTask() {
    // start up Tasks screen
    val ac=vityScenario = Ac=vityScenario.launch(TasksAc=vity::class.java)
    dataBindingIdlingResource.monitorAc=vity(ac=vityScenario)
    // Click on the "+" bu\on, add details, and save
    onView(withId(R.id.fab_add_task)).perform(click())
    onView(withId(R.id.add_task_=tle)).perform(typeText("=tle"))
    onView(withId(R.id.add_task_descrip=on)).perform(typeText("descrip=on"))
    onView(withId(R.id.fab_save_task)).perform(click())
    // Then verify task is displayed on screen
    onView(withText("=tle")).check(matches(isDisplayed()))
    }

    View Slide

  19. E2E Test Code
    @Test
    fun createTask() {
    // start up Tasks screen
    val ac=vityScenario = Ac=vityScenario.launch(TasksAc=vity::class.java)
    dataBindingIdlingResource.monitorAc=vity(ac=vityScenario)
    // Click on the "+" bu\on, add details, and save
    onView(withId(R.id.fab_add_task)).perform(click())
    onView(withId(R.id.add_task_=tle)).perform(typeText("=tle"))
    onView(withId(R.id.add_task_descrip=on)).perform(typeText("descrip=on"))
    onView(withId(R.id.fab_save_task)).perform(click())
    // Then verify task is displayed on screen
    onView(withText("=tle")).check(matches(isDisplayed()))
    }
    Ac=vityΛىಈ͠

    ෳ਺ͷFragmentʹ·͕ͨͬͯ
    ϑϩʔΛݕূ

    View Slide

  20. Integra=on Test

    View Slide

  21. Integra=on Test Scope
    ςετͷείʔϓΛ
    খ͘͢͞Δ

    View Slide

  22. Test Double
    ্ʹ͍͘΄Ͳ࣮ࡍͷ

    ৼΔ෣͍ʹۙ͘ͳΔ
    Fidelity

    View Slide

  23. AddEditTaskFragmentTest
    VerifyͰ͖ΔΑ͏

    ϞοΫʹஔ͖׵͑
    ࠓճ͸಺෦ʹMapΛ࣋ͭ
    Fakeʹஔ͖׵͑


    View Slide

  24. Integra=on Test Code
    @Test
    fun validTask_isSaved() {
    // GIVEN - On the "Add Task" screen.
    val navController = mock(NavController::class.java)
    val bundle = AddEditTaskFragmentArgs(null,
    getApplica=onContext().getString(R.string.add_task)).toBundle()
    val scenario = launchFragmentInContainer(bundle, R.style.AppTheme)
    scenario.onFragment {
    Naviga=on.setViewNavController(it.view!!, navController)
    }

    ..
    }
    FragmentΛىಈ͠

    ϞοΫͷNavControllerΛ

    ౉͢

    View Slide

  25. Integra=on Test Code
    @Test
    fun validTask_isSaved() {

    ..
    // WHEN - Valid =tle and descrip=on combina=on and click save
    onView(withId(R.id.add_task_=tle)).perform(replaceText("=tle"))
    onView(withId(R.id.add_task_descrip=on)).perform(replaceText("descrip=on"))
    onView(withId(R.id.fab_save_task)).perform(click())
    ..
    }

    View Slide

  26. Integra=on Test Code
    @Test
    fun validTask_isSaved() {
    ..
    // THEN - Verify that the repository saved the task
    val tasks = (repository.getTasksBlocking(true) as Result.Success).data
    assertEquals(tasks.size, 1)
    assertEquals(tasks[0].=tle, "=tle")
    verify(navController).navigate(
    AddEditTaskFragmentDirec=ons
    .ac=onAddEditTaskFragmentToTasksFragment(ADD_EDIT_RESULT_OK))
    }
    Fake͔ΒσʔλΛ
    औಘͯ͠ݕূ


    View Slide

  27. Integra=on Test Code
    @Test
    fun validTask_isSaved() {
    ..
    // THEN - Verify that the repository saved the task
    val tasks = (repository.getTasksBlocking(true) as Result.Success).data
    assertEquals(tasks.size, 1)
    assertEquals(tasks[0].=tle, "=tle")
    verify(navController).navigate(
    AddEditTaskFragmentDirec=ons
    .ac=onAddEditTaskFragmentToTasksFragment(ADD_EDIT_RESULT_OK))
    }
    ਖ਼͍͠ύϥϝʔλͰNaviga=on
    Ͱ͖͍ͯΔ͔ݕূ

    View Slide

  28. Unit Test

    View Slide

  29. Unit Test Summary
    TaskLocalDataStoreTest
    ৄࡉͳ࣮૷ΑΓͲ͏ৼΔ෣͏͔
    DaoΛMockͰ͸ͳ͘Fakeʹͯ͠

    ςετ
    Corou=neͷςετ༻ͷrunBlockingTest

    View Slide

  30. Tes=ng Codelab
    ηογϣϯͷ಺༰Λ࣮ࡍʹखΛಈ͔
    ͠ͳ͕ΒֶͿ͜ͱ͕Ͱ͖Δ
    g.co/codelabs/android-tes=ng

    View Slide

  31. AndroidX Test
    Local TestͱInstrumenta=on Testͷࠩ෼Λٵऩ͢Δ

    unified API
    ҆ఆੑ΍૬ޓӡ༻ੑͷڧԽ
    Local JVM supportͷڧԽ
    Architecture component support

    View Slide

  32. Nitrogen
    Unified APIͰॻ͔ΕͨςετΛ

    ༷ʑͳ؀ڥͰγʔϜϨεʹ

    ࣮ߦͰ͖ΔPlanorm

    View Slide

  33. Nitrogen͕ϦϦʔε͞ΕΔ·Ͱ
    android {
    sourceSets {
    String sharedTestDir = 'src/sharedTest/java'
    test {
    java.srcDir sharedTestDir
    }
    androidTest {
    java.srcDir sharedTestDir
    }
    }

    View Slide

  34. CodelabͰͷ࢖͍෼͚
    androidTest
    E2E
    sharedTest
    Integra=on Test(FragmentɾRoom·ΘΓ)
    test
    Unit Test

    View Slide

  35. Nitrogen EAP
    2019 2Qʹalpha൛ϦϦʔε༧ఆͳͷͰ
    ͦΖͦΖ৮ΕΔΑ͏ʹͳΔͱظ଴
    bit.ly/nitrogen-eap

    View Slide

  36. ·ͱΊ
    ࣗಈςετͷઓུͱͦΕΛ࣮ݱ͢ΔArchitectuteͷ঺հ
    CodelabΛ௨ͯ͡खΛಈ͔͠ͳ͕ΒֶͿ͜ͱ͕Ͱ͖Δ
    Unified APIΛ࢖༻ͨ͠γʔϜϨεͳ࣮ߦ(Nitrogen EAP!)

    View Slide

  37. Session Info
    h\ps://events.google.com/io/schedule/events/
    7bd73b37-1bca-40b3-b8e9-6efe3a9ac234
    h\ps://www.youtube.com/watch?v=VJi2vmaQe6w

    View Slide