Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

$ 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

Slide 3

Slide 3 text

% 3 Espresso( Android ) "*! AndroidX) Kotlin& AAC (Android Architecture Components) +#& Kotlin AndroidX AAC Android$& % '

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

: 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/).

Slide 7

Slide 7 text

: Espresso API Espresso OK perform()check()%# 7 onView(ViewMatcher).perform(ViewAction) .check(ViewAssertion) ViewMatcherViewViewAction → ViewViewAssertion$ "!

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

: AAC (Android Architecture Components) %&" " ! #$ https://d.android.com/topic/libraries/architecture/ p 2018 Android Jetpack 10 pViewModel pLiveData pRoom pNavigation pPager pWorkManager...

Slide 11

Slide 11 text

;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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

: p Android Studio 3.3 p p Use AndroidX artifacts p (LanguageKotlin) 13 AndroidX

Slide 14

Slide 14 text

: p Android Studio RefactorMigrate to AndroidX... 14 AndroidX

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

KotlinjvmTarget 1.8 activityScenarioRule(intent: Intent? = null) 18 AndroidX

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

,%*%): 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.

Slide 22

Slide 22 text

Sunflower My gardenPlant list 22

Slide 23

Slide 23 text

Sunflower Plant list → Plant details 23

Slide 24

Slide 24 text

Sunflower Plant details FAB → My garden 24

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

Espresso Test Recorder: 27 KotlinAndroidX p p AndroidX Kotlin

Slide 28

Slide 28 text

Espresso Test Recorder: "&# p /adb'! p % Android Studio p RunRecord Espresso Test p %$ p Record Your Test 28 AndroidX Kotlin

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Espresso Test Recorder: Assertion p Record Your Test Add Assertion 30 AndroidX Kotlin

Slide 31

Slide 31 text

Espresso Test Recorder: Assertion p 31 AndroidX Kotlin

Slide 32

Slide 32 text

Espresso Test Recorder: Assertion p p Edit assertionSave Assertion 32 AndroidX Kotlin

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Espresso Test Recorder: ATSL or AndroidX Test? Espresso Test Recorder () AndroidX AndroidX Test ATSL (Android Testing Support Library) AndroidX 34 AndroidX Kotlin

Slide 35

Slide 35 text

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 ()

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Espresso Test Recorder: ActivityTestRule ActivityScenarioRule 38 AndroidX Kotlin

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

ActivityTestRule vs ActivityScenarioRule Activity p ActivityTestRule 42 @get:Rule val activityTestRule = ActivityTestRule(GardenActivity::class.java, false, false) ... // Activity activityTestRule.launchActivity(null) AndroidX Kotlin

Slide 43

Slide 43 text

ActivityTestRule vs ActivityScenarioRule Activity p ActivityScenario (ActivityScenarioRule) 43 // Rule // Activity launchActivity(null).use { ... // ActivityRESUMED } AndroidX Kotlin

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Espresso Test Recorder! 47 Recorder " p % p $ Kotlin PageObject (#)

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

#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

Slide 54

Slide 54 text

54 Kotlin : PageObject

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

57 Step1 Step2-4 Page ( ) Step5 return value PageObject

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Step1 Page 59 public Extract Method Opt + Command + M Kotlin

Slide 60

Slide 60 text

Step1 : droidkaigi/step1 60 Kotlin

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

Step2 62 Move Line Down Opt + Shift + ↓ Kotlin

Slide 63

Slide 63 text

Step2 : droidkaigi/step2 63 Kotlin

Slide 64

Slide 64 text

Step3 64 RefactorMove F6 Kotlin

Slide 65

Slide 65 text

Step3 ! ! 65 Move Page Kotlin

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

Step3 67 : droidkaigi/step3 Move () Kotlin

Slide 68

Slide 68 text

Step4 object 68 object Kotlin

Slide 69

Slide 69 text

Step4 object 69 Show Intention Actions Show Intention Actions Opt + Enter Kotlin

Slide 70

Slide 70 text

Step4 object 70 : droidkaigi/step4 Page Kotlin

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

Step5 return 72 PageObject return Show Intention Actions Opt + Enter Kotlin

Slide 73

Slide 73 text

Step5 return 73 : droidkaigi/step5 return type Kotlin

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

75 Kotlin

Slide 76

Slide 76 text

76 Kotlin

Slide 77

Slide 77 text

77 Page OK Kotlin

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

p Espresso Test Recorder PageObject p Android Studio () p Kotlin Move 79 Kotlin

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

AAC ! p Room: p LiveData RxJava ! p Pager: p LivePagedListBuilderRxPagedListBuilder p WorkManager: 82 ArchTaskExecutor Executors. newFixedThreadPool() AAC

Slide 83

Slide 83 text

AAC ! p Room: p LiveData RxJava ! p Pager: p LivePagedListBuilderRxPagedListBuilder p WorkManager: 83 ArchTaskExecutor Executors. newFixedThreadPool() AAC

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

AAC ! p Room: p LiveData RxJava ! p Pager: p LivePagedListBuilderRxPagedListBuilder p WorkManager: 87 ArchTaskExecutor Executors. newFixedThreadPool() AAC

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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 )

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

p AAC(!% '" p ArchTaskExecutor& p $#Executor& p Kotlin & 91

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

$# 93 p EspressoAndroidXKotlin )"*! p AndroidX) Recorder OK p ,+ 'deprecated -& (AndroidJUnit4ActivityScenarioRule) p PageObject Kotlin (%

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

"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

Slide 97

Slide 97 text

97 Espresso