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

EspressoのテストをAndroidの最新トレンドに対応させよう / Make Espresso testing follow the cutting edge in Android development

TOYAMA Sumio
February 07, 2019

EspressoのテストをAndroidの最新トレンドに対応させよう / Make Espresso testing follow the cutting edge in Android development

DroidKaigi 2019のセッション「EspressoのテストをAndroidの最新トレンドに対応させよう」の発表スライドです。
サンプルコードのGitHubリポジトリは
https://github.com/sumio/android-sunflower-for-droidkaigi2019
です。

TOYAMA Sumio

February 07, 2019
Tweet

More Decks by TOYAMA Sumio

Other Decks in Programming

Transcript

  1. Espresso

    Android

    2019.02.07
    (sumio_tym)
    DroidKaigi 2019 @ Day1 Room2 12:50-

    View Slide

  2. $
    p : # (TOYAMA Sumio)
    @sumio_tym (Twitter) / @sumio (GitHub)
    p : DeNA SWET
    (Software Engineer in Test)
    p :

    p : Android
    "!
    https://peaks.cc/sumio_tym/android_testing
    2

    View Slide

  3. %

    3
    Espresso(
    Android
    ) "*!


    AndroidX)
    Kotlin&
    AAC (Android Architecture Components)
    +#&
    Kotlin
    AndroidX
    AAC
    Android$&

    %
    '

    View Slide

  4. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    4
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  5. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    5
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  6. : Espresso
    p Android'+ UI
    https://developer.android.com/training/testing/espresso/
    p ATSL (Android Testing Support Library)AndroidX Test #
    p % -$
    p Espresso Test Recorder)


    p ".
    p * /!(,&
    6
    ()
    () This logo image is reproduced from work created and shared by the Android Open Source Project
    (https://developer.android.com/license) and used according to terms described in the Creative Commons 2.5
    Attribution License (http://creativecommons.org/licenses/by/2.5/).

    View Slide

  7. : Espresso API
    Espresso OK
    perform()check()%#
    7
    onView(ViewMatcher).perform(ViewAction)
    .check(ViewAssertion)
    ViewMatcherViewViewAction

    ViewViewAssertion$ "!

    View Slide

  8. : Espresso API
    Espresso!OK
    $API (%"
    https://d.android.com/training/testing/espresso/cheat-sheet
    8
    onView(ViewMatcher).perform(ViewAction)
    .check(ViewAssertion)
    ViewMatcherViewViewAction
    → ViewViewAssertion'
    %"

    View Slide

  9. : AndroidX
    AndroidX = Support Library
    + Android Testing Support Library
    + Jetpack components
    https://d.android.com/jetpack/androidx/
    p androidx.*
    p
    p Android Studio


    9

    View Slide

  10. : AAC (Android Architecture Components)
    %&" " !

    #$
    https://d.android.com/topic/libraries/architecture/
    p 2018 Android Jetpack

    10
    pViewModel
    pLiveData
    pRoom
    pNavigation
    pPager
    pWorkManager...

    View Slide


  11. ;8

    p Espresso
    p CN62I?H$-AMO95
    p API#%!%1J
    p AndroidX
    p D/
    >3B4G7 ("+&.F0 *%
    p AAC (Android Architecture Components)
    p E:=),@K<.*'.%L
    11

    View Slide

  12. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    12
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  13. :

    p Android Studio 3.3
    p

    p Use AndroidX artifacts
    p (LanguageKotlin)
    13
    AndroidX

    View Slide

  14. :
    p Android Studio

    RefactorMigrate to AndroidX...
    14
    AndroidX

    View Slide

  15. build.gradle
    15
    android {
    ...
    testInstrumentationRunner
    "androidx.test.runner.AndroidJUnitRunner"
    }
    dependencies {
    ...
    androidTestImplementation
    'androidx.test:runner:...'
    androidTestImplementation
    'androidx.test.espresso:espresso-core:...'
    AndroidX

    View Slide



  16. 16
    Latest Stable
    Version()
    androidx.test:runner 1.1.1
    androidx.test:rules 1.1.1
    androidx.test.ext:junit-ktx 1.1.0
    androidx.test:core-ktx 1.1.0
    androidx.test.espresso:espresso-core 3.1.1
    androidx.test.espresso:espresso-contrib 3.1.1
    ()2019127
    ActivityScenario ()
    AndroidJUnit4()
    ActivityScenarioRule ()
    AndroidX

    View Slide

  17. KotlinjvmTarget 1.8
    : androidx.test.ext:junit-ktxJVM 1.7
    17
    tasks.withType(org.jetbrains.kotlin.gradle.tasks.
    KotlinCompile).all {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
    kotlinOptions {
    jvmTarget = '1.8'
    }
    }
    AndroidX

    View Slide

  18. KotlinjvmTarget 1.8


    activityScenarioRule(intent: Intent? = null)



    18
    AndroidX

    View Slide




  19. p )(AndroidX'Espresso
    p &$" Use AndroidX artifacts
    p .+ Migrate to AndroidX...
    p - ,
    %
    #*!
    p androidx.test.ext:junit-ktx
    p androidx.test:core-ktx
    19
    AndroidX

    View Slide

  20. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    20
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  21. ,%*%): Android Sunflower
    p Android Jetpack (≒AAC) 2
    ,%*%)
    https://github.com/googlesamples/android-sunflower
    p %+(&,/3Kotlin6-
    21
    " #1 (04 )

    .5 ($Android Sunflower%) ),'!#78

    Copyright 2018 Google, Inc. Licensed under the Apache License, Version 2.0.
    Select text used for describing the plants are used from Wikipedia via CC BY-SA 3.0 US.

    View Slide

  22. Sunflower

    My gardenPlant list
    22

    View Slide

  23. Sunflower

    Plant list → Plant details
    23

    View Slide

  24. Sunflower
    Plant details FAB → My garden

    24

    View Slide

  25. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    25
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  26. Espresso Test Recorder: #
    p Android Studio 3.2
    (AndroidXKotlin!)
    p "
    p adb$Android/

    26
    AndroidX
    Kotlin

    View Slide

  27. Espresso Test Recorder:

    27
    KotlinAndroidX
    p
    p

    AndroidX
    Kotlin

    View Slide

  28. Espresso Test Recorder: "&#
    p /adb'!
    p %

    Android Studio
    p RunRecord Espresso Test
    p %$
    p Record Your Test
    28
    AndroidX
    Kotlin

    View Slide

  29. Espresso Test Recorder: '
    "
    p ! '
    p 'Record Your Test %
    ($&
    1
    #()
    29
    AndroidX
    Kotlin

    View Slide

  30. Espresso Test Recorder: Assertion
    p Record Your Test

    Add Assertion
    30
    AndroidX
    Kotlin

    View Slide

  31. Espresso Test Recorder: Assertion
    p



    31
    AndroidX
    Kotlin

    View Slide

  32. Espresso Test Recorder: Assertion
    p

    p Edit assertionSave Assertion
    32
    AndroidX
    Kotlin

    View Slide

  33. Espresso Test Recorder:
    p Record Your Test OK

    p Specify a test class for your test
    p Test class name: ()
    p Test class language: Kotlin
    33
    AndroidX
    Kotlin

    View Slide

  34. Espresso Test Recorder: ATSL or AndroidX Test?
    Espresso Test Recorder
    ()
    AndroidX
    AndroidX Test

    ATSL (Android Testing Support Library)
    AndroidX

    34
    AndroidX
    Kotlin

    View Slide

  35. Espresso Test Recorder:
    35
    Espresso Test Recorder
    p AndroidJUnit4deprecated
    p ActivityTestRuledeprecated

    () This text is reproduced from work created and shared by the Android Open Source Project
    (https://developer.android.com/license) and used according to terms described in Apache License, Version 2.0.
    URL: https://d.android.com/reference/androidx/test/ext/junit/rules/ActivityScenarioRule
    ()

    View Slide

  36. Espresso Test Recorder:
    deprecatedAndroidJUnit4 (
    import ( )
    36
    import androidx.test.runner.AndroidJUnit4
    import androidx.test.ext.junit.runners.AndroidJUnit4
    AndroidX
    Kotlin

    View Slide

  37. Espresso Test Recorder:
    deprecatedAndroidJUnit4
    import Show Intention Actions
    (Option+Enter)

    37
    AndroidX
    Kotlin

    View Slide

  38. Espresso Test Recorder:
    ActivityTestRule

    ActivityScenarioRule
    38
    AndroidX
    Kotlin

    View Slide

  39. Espresso Test Recorder:
    39
    @Rule
    @JvmField
    var mActivityTestRule =
    ActivityTestRule(GardenActivity::class.java)
    @get:Rule
    val activityScenarioRule =
    activityScenarioRule()
    Java getter

    API: activityScenarioRule
    read-only
    AndroidX
    Kotlin

    View Slide

  40. activityScenarioRule
    40
    @get:Rule
    val activityScenarioRule =
    ActivityScenarioRule(GardenActivity::class.java)
    @get:Rule
    val activityScenarioRule =
    activityScenarioRule()
    Kotlin
    jvmTarget 1.8
    AndroidX
    Kotlin

    View Slide

  41. ActivityTestRule vs ActivityScenarioRule
    Activity

    p ActivityTestRule
    p ActivityScenarioRule
    41
    @get:Rule
    val activityTestRule =
    ActivityTestRule(GardenActivity::class.java)
    @get:Rule
    val activityScenarioRule =
    activityScenarioRule()
    AndroidX
    Kotlin

    View Slide

  42. ActivityTestRule vs ActivityScenarioRule
    Activity

    p ActivityTestRule
    42
    @get:Rule
    val activityTestRule =
    ActivityTestRule(GardenActivity::class.java,
    false, false)
    ...
    // Activity

    activityTestRule.launchActivity(null)
    AndroidX
    Kotlin

    View Slide

  43. ActivityTestRule vs ActivityScenarioRule
    Activity


    p ActivityScenario (ActivityScenarioRule)
    43
    // Rule

    // Activity
    launchActivity(null).use {
    ...
    //

    ActivityRESUMED
    }
    AndroidX
    Kotlin

    View Slide

  44. ActivityTestRule vs ActivityScenarioRule
    ActivityScenario Activity
    /
    44
    // Activity
    launchActivity(null)
    .use { scenario ->
    scenario.moveToState(State.STARTED)
    }
    AndroidX
    Kotlin
    scenario.onActivity { ... }
    (Espresso)

    View Slide


  45. p Recorder"%*#
    (
    )
    p "%') Test class languageKotlinOK
    p AndroidX&AndroidX
    Test
    p 2!
    $
    p AndroidJUnit4 (
    p ActivityTestRule → ActivityScenario / ActivityScenarioRule
    45
    AndroidX
    Kotlin

    View Slide

  46. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    46
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  47. Espresso Test Recorder!
    47
    Recorder
    "
    p %


    p $
    Kotlin
    PageObject

    (#)

    View Slide

  48. PageObject$$
    2'(,4
    https://github.com/SeleniumHQ/selenium/wiki/PageObjects
    p %6#(Page )&5
    p Page#:
    !
    p "$(9/) or )0.+*-
    p !8
    7 Page

    p 3Page#!1
    48
    Kotlin

    View Slide

  49. Sunflower PageObject
    MyGardenPage
    p //
    goPlantList(): PlantListPage
    p // plantName
    //
    assertPlanted(plantName: String):
    MyGardenPage
    49
    Kotlin

    View Slide

  50. Sunflower PageObject
    PlantListPage
    p // plantName
    //
    showPlantDetail(plantName: String):
    PlantDetailPage
    p //
    goBackMyGarden(): MyGardenPage
    50
    Kotlin

    View Slide

  51. Sunflower PageObject
    PlantDetailPage
    p // ) )(
    goBackPlantList(): PlantListPage
    p /
    addToMyGarden(): PlantDetailPage
    51
    Kotlin

    View Slide

  52. &0: (. PageObject)/
    p % $#!#
    https://github.com/sumio/android-sunflower-for-droidkaigi2019
    p , "%*
    +'-
    1
    52
    Kotlin

    View Slide

  53. #3: *0
    PageObject ,1
    *0
    ( !&)
    (: droidkaigi/step0
    1. PlantList .$
    2. "Avocado" " (PlantDetail .$ )
    3. FAB2My Garden /'
    4. PlantDetail 4
    5. My Garden 4
    6. "Avocado"%+-)
    53
    Kotlin

    View Slide


  54. 54
    Kotlin
    :
    PageObject

    View Slide

  55. : PageObject

    Android Studio
    p Move Line Up/Down
    p RefactorExtractMethod
    p RefactorExtractVariable
    55
    Opt + Shift + ↑/↓
    Opt + Command + M
    Opt + Command + V
    macOS
    Kotlin

    View Slide

  56. : PageObject
    ! Android Studio
    p RefactorInline
    p RefactorMove (
    )
    p Show Intention Actions
    56
    macOS
    F6
    Opt + Enter
    Opt + Command + N
    Kotlin

    View Slide


  57. 57
    Step1

    Step2-4 Page

    ( )
    Step5 return value
    PageObject



    View Slide

  58. 58
    Step1
    Step2-4
    Page
    ( )
    Step5 return value

    PageObject


    View Slide

  59. Step1

    Page

    59
    public

    Extract Method
    Opt + Command + M
    Kotlin

    View Slide

  60. Step1

    : droidkaigi/step1
    60
    Kotlin

    View Slide

  61. 61
    Step1
    Step2-4
    Page
    ( )
    Step5 return value

    PageObject


    View Slide

  62. Step2



    62
    Move Line Down
    Opt + Shift + ↓
    Kotlin

    View Slide

  63. Step2

    : droidkaigi/step2
    63


    Kotlin

    View Slide

  64. Step3


    64
    RefactorMove
    F6
    Kotlin

    View Slide

  65. Step3 !
    !

    65
    Move

    Page
    Kotlin

    View Slide

  66. Step3

    66


    childAtPosition (Recorder) PageUtils.kt
    goPlantList MyGardenPage.kt
    assertPlanted
    showPlantDetail PlantListPage.kt
    goBackMyGarden
    goBackPlantList PlantDetailPage.kt
    addToMyGarden
    Kotlin

    View Slide

  67. Step3

    67
    : droidkaigi/step3
    Move

    ()
    Kotlin

    View Slide

  68. Step4 object
    68

    object
    Kotlin

    View Slide

  69. Step4 object
    69
    Show Intention Actions


    Show Intention Actions
    Opt + Enter
    Kotlin

    View Slide

  70. Step4 object
    70

    : droidkaigi/step4
    Page
    Kotlin

    View Slide

  71. 71
    Step1
    Step2-4
    Page
    ( )
    Step5 return value

    PageObject


    View Slide

  72. Step5
    return
    72

    PageObject return
    Show Intention Actions
    Opt + Enter
    Kotlin

    View Slide

  73. Step5

    return
    73
    : droidkaigi/step5
    return type
    Kotlin

    View Slide

  74. 74
    Step1
    Step2-4
    Page
    ( )
    Step5 return value

    PageObject


    View Slide

  75. 75
    Kotlin



    View Slide


  76. 76

    Kotlin

    View Slide


  77. 77

    Page

    OK
    Kotlin

    View Slide

  78. :

    p MyGardenPage.assertPlanted()
    ( '!'#
    &
    p "%$: droidkaigi/extra
    78
    Kotlin

    View Slide



  79. p Espresso Test Recorder

    PageObject
    p Android Studio


    ()
    p Kotlin Move
    79
    Kotlin

    View Slide

  80. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    80
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  81. % 5'#UI13
    *
    p View.9
    p ;)"/(ID )+8,$
    p
    2
    p 4!(-&7:<60

    81
    AAC
    AAC

    View Slide

  82. AAC !
    p Room:
    p LiveData RxJava !
    p Pager:

    p LivePagedListBuilderRxPagedListBuilder
    p WorkManager:
    82
    ArchTaskExecutor
    Executors.
    newFixedThreadPool()
    AAC

    View Slide

  83. AAC !
    p Room:
    p LiveData RxJava !
    p Pager:

    p LivePagedListBuilderRxPagedListBuilder
    p WorkManager:
    83
    ArchTaskExecutor
    Executors.
    newFixedThreadPool()
    AAC

    View Slide

  84. ArchTaskExecutor
    84
    p AAC
    p
    CountingTaskExecutorRule
    androidx.arch.core:core-testing:2.0.0
    AAC

    View Slide

  85. ArchTaskExecutor:
    85
    AAC
    @get:Rule
    val countingRule = CountingTaskExecutorRule()
    @Test
    fun myTest() {
    onView(...).perform(...)
    // AAC
    countingRule..drainTasks(1, TimeUnit.MINUTES)
    onView(...).perform(...)
    ...
    }


    View Slide

  86. ArchTaskExecutor: G S
    86
    p TaskExecutorWithIdlingResourceRule

    p B / .
    /
    AAC
    @get:Rule
    val executorRule =
    TaskExecutorWithIdlingResourceRule()
    Rule L: OK

    View Slide

  87. AAC !
    p Room:
    p LiveData RxJava !
    p Pager:

    p LivePagedListBuilderRxPagedListBuilder
    p WorkManager:
    87
    ArchTaskExecutor
    Executors.
    newFixedThreadPool()
    AAC

    View Slide

  88. ArchTaskExecutor
    :
    88
    ThreadPoolExecutor
    p WorkManager
    import androidx.work.Configuration
    ...
    val executor = ...
    val configuration = Configuration.Builder()
    .setExecutor(executor)
    .build()
    WorkManager.initialize(context, configuration)
    AAC

    View Slide

  89. ArchTaskExecutor: # $'
    !
    89
    IdlingThreadPoolExecutor"%
    androidx.test.espresso.idling:idling-concurrent:3.1.1
    val executor = IdlingThreadPoolExecutor(...)
    val configuration = Configuration.Builder()
    .setExecutor(executor)
    .build()
    WorkManager.initialize(context, configuration)
    AAC
    &
    ( https://bit.ly/2Biu8QJ )

    View Slide

  90. ArchTaskExecutor: "
    90


    (: CoroutineWorker)
    asCoroutineDispatcher()
    class MyWorker(...): CoroutineWorker(...) {
    override val coroutineContext:
    CoroutineDispatcher
    get() {
    val executor = IdlingThreadPoolExecutor(...)
    return executor.asCoroutineDispatcher()
    }
    ...
    AAC
    CoroutineWorker
    !

    View Slide


  91. p AAC(!%


    '"
    p ArchTaskExecutor&

    p $#Executor&

    p Kotlin &

    91

    View Slide

  92. %*
    p #'&+
    p Espresso
    p "! : Android Sunflower
    p Espresso Test Recorder)
    p PageObject""
    (,
    p AAC-$ ("
    p
    92
    Kotlin
    AndroidX
    AAC
    Kotlin
    AndroidX

    View Slide

  93. $#
    93
    p EspressoAndroidXKotlin
    )"*!

    p AndroidX) Recorder OK
    p ,+ 'deprecated -&
    (AndroidJUnit4ActivityScenarioRule)
    p PageObject
    Kotlin
    (%

    View Slide

  94. !
    94
    p AAC
    Espresso%
    *"( )
    p
    '
    $

    View Slide

  95. URL
    p AndroidX Test
    https://github.com/android/android-test
    p Yuki AnzaiAndroidX Testin Android Dev Summit
    2018
    https://speakerdeck.com/yanzm/androidx-test
    p AndroidX Test

    https://developer.android.com/jetpack/androidx/releases/test
    95

    View Slide

  96. "URL (
    (
    +)
    p !) Espresso %
    in DroidKaigi 2017
    https://droidkaigi.github.io/2017/timetable.html#session-26
    p Espresso
    $ 'in
    DroidKaigi 2018
    https://droidkaigi.jp/2018/timetable?session=16928
    p *Android(+)
    https://peaks.cc/sumio_tym/android_testing
    96

    View Slide

  97. 97
    Espresso





    View Slide