Slide 1

Slide 1 text

Robolectric UI     2020.03.17 (DeNA.apk #2 Day 2)   (sumio_tym)

Slide 2

Slide 2 text

$ 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

Slide 3

Slide 3 text

)  3 Espresso-  Robolectric'   /%  p Robolectric!'* p Robolectric.+1$0 " #& p Robolectric'(,'(, p Robolectric'  UI

Slide 4

Slide 4 text

%) 4 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 5

Slide 5 text

%) 5 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 6

Slide 6 text

   6 This image is reproduced from work (https://d.android.com/training/testing/fundamentals) created and shared by the Android Open Source Project (https://d.android.com/license) and used according to terms described in the Creative Commons 2.5 Attribution License (https://creativecommons.org/licenses/by/2.5/).

Slide 7

Slide 7 text

   7 This image is reproduced from work (https://d.android.com/training/testing/fundamentals) created and shared by the Android Open Source Project (https://d.android.com/license) and used according to terms described in the Creative Commons 2.5 Attribution License (https://creativecommons.org/licenses/by/2.5/). Local Test PC Instrumented Test Android     UI Tests   Integration Tests Unit Tests  

Slide 8

Slide 8 text

Local TestInstrumented Test  8 Local Test Instrumented Test   PC (JVM) Android    Android API   (Espresso API)       src/test src/androidTest

Slide 9

Slide 9 text

Local Test Instrumented Test  9 Local Test Instrumented Test   PC (JVM) Android    Android API    (Espresso API)        src/test src/androidTest   

Slide 10

Slide 10 text

+25-Robolectric 10 p JVM# Android !',   (http://robolectric.org/) p *&!: 4.3.1 p 3rd party3/ Android)0  p $Instrumented/Local Test1% (   Robolectric.4"(Project Nitrogen) () () Copyright 6 2010 Xtreme Labs, Pivotal Labs and Google Inc. This logo image (https://github.com/robolectric/robolectric/blob/master/images/robolectric-horizontal.png) is licensed under The MIT License (https://github.com/robolectric/robolectric/blob/master/LICENSE).

Slide 11

Slide 11 text

$*-% Robolectric 11 p JVMAndroid   (http://robolectric.org/) p #  : 4.3.1 p 3rd party +' Android "( p  Instrumented/Local Test ) !   Robolectric &, (Project Nitrogen) () () Copyright . 2010 Xtreme Labs, Pivotal Labs and Google Inc. This logo image (https://github.com/robolectric/robolectric/blob/master/images/robolectric-horizontal.png) is licensed under The MIT License (https://github.com/robolectric/robolectric/blob/master/LICENSE).

Slide 12

Slide 12 text

Local TestRobolectric 3.x   12 Local Test /w Robolectric 3.x Instrumented Test   PC (JVM) Android    Android API   (Espresso API)        src/test src/androidTest

Slide 13

Slide 13 text

Local TestRobolectric 4.x  13 Local Test /w Robolectric 4.x Instrumented Test    PC (JVM) Android    Android API   (Espresso API)          src/test src/androidTest

Slide 14

Slide 14 text

Robolectric 4.x  14 Local Test UI Test   p   UI Test   p UI Test    (  ) This image is a modification based on work (https://d.android.com/training/testing/fundamentals ) created and shared by the Android Open Source Project (https://d.android.com/license) and used according to terms described in the Creative Commons 2.5 Attribution License (https://creativecommons.org/licenses/by/2.5/).

Slide 15

Slide 15 text

Robolectric 4.xEspresso Support*1 15 p Robolectric 4.x Espresso Support p 74UI Test ).   p Robolectric32( ) 5+ %/ p "!$0 p #!&6-'  ,

Slide 16

Slide 16 text

Robolectric 4.x(  16 p Robolectric  &  p *% "'  $&  p Robolectric! (#))   p Robolectric(#))  $,- +  

Slide 17

Slide 17 text

%) 17 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 18

Slide 18 text

Robolectric (05) 18 p JVM% Android ! #)- !" (http://robolectric.org/) p ,( #: 4.3.1 p 3rd party3/Android+1!" p &Instrumented/Local Test2' *  Robolectric.4$(Project Nitrogen) () () Copyright 6 2010 Xtreme Labs, Pivotal Labs and Google Inc. This logo image (https://github.com/robolectric/robolectric/blob/master/images/robolectric-horizontal.png) is licensed under The MIT License (https://github.com/robolectric/robolectric/blob/master/LICENSE).

Slide 19

Slide 19 text

Robolectric: Android! ,) 19 1. Android (jar)!  https://mvnrepository.com/artifact/org.robolectric/android-all 2. ! Android%- 3. %#$ *    p Robolectric&'Shadow   Shadow  p Shadow+ "(  

Slide 20

Slide 20 text

Robolectric: Shadow   20 p Android +  #+$( p native*  p Android*  p etc. p " , API !& p ': logcat  )% ShadowLog.stream = System.out

Slide 21

Slide 21 text

Robolectric: 2  (0/ .6 21 ( @Before p 4'#!%" onCreate1 p static$ (+&  ,-) p JVM5 SQLite(3(sqlite4java))* p  $ ( )

Slide 22

Slide 22 text

Robolectric:  ! 22    (LEGACY) p     (  ) p   (AsyncTask ) (  )

Slide 23

Slide 23 text

Robolectric: 23 &%(PAUSED) p  !( p ShadowLooper.getShadowMainLooper().idle() p SystemClock )' $  SystemClock.sleep()( * #" p  !(  )

Slide 24

Slide 24 text

Robolectric: LEGACY# PAUSED# 24 Looper Mode46Robolectric4.3PAUSED2' LEGACY p $!(/0&  5+ p )-"%* 3  UI   , PAUSED p )-"%* .1  p EspressoUI 

Slide 25

Slide 25 text

Robolectric: LEGACY(%PAUSED(% 25 Looper Mode PAUSED 4:

Slide 26

Slide 26 text

Robolectric:    26 p Robolectric "( p JVM! Android ! p Android & Shadow )+ p $"  p Espresso PAUSED Looper Mode p    #'% &"  p '% $(,*$-   p   # 

Slide 27

Slide 27 text

%) 27 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 28

Slide 28 text

  : build.gradle 28 android { ... testOptions { unitTests.includeAndroidResources = true } } Robolectric 

Slide 29

Slide 29 text

 : () 29    Latest Stable Version() androidx.test:core-ktx 1.2.0 androidx.test.ext:junit-ktx 1.1.1 androidx.test.espresso:espresso-contrib 3.2.0 org.robolectric:robolectric 4.3.1  testImplementation  ()2020/03/01

Slide 30

Slide 30 text

 :  30 p src/test   p src/androidTest  

Slide 31

Slide 31 text

 :    31 @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) class MyRobolectricTest { @get:Rule val activityScenarioRule = activityScenarioRule() ... } PAUSED Looper Mode  ActivityTestRule

Slide 32

Slide 32 text

 :  32 p targetSdkVersion29JDK9 p JDK8Robolectric 28 @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @Config(sdk = 28) class MyRobolectricTest { ... }

Slide 33

Slide 33 text

:    33 p Local TestRobolectricEspresso  OK p  p Instrumented Test  p PAUSED Looper Mode p ActivityTestScenario  p   SDK  

Slide 34

Slide 34 text

%) 34 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 35

Slide 35 text

9' : Espresso(.)0/8*57; 35 Instrumented Robolectric $&% #   AsyncTask   IdlingResource   <)06- 31>+=?9   Espresso(.)0/8 A DroidKaigi 2018,2  Espresso!"#)06-:( http://bit.ly/2QdCeBt )4@  

Slide 36

Slide 36 text

: Robolectric   36 !1: View '(   p +  p Instrumented Test  $#   !2: RobolectricEspresso " & p Instrumented Test  $#  p Espresso ,*(OSS)  p Robolectric )%

Slide 37

Slide 37 text

- % : Robolectric- %  37 !1: View '(   p +'( p Instrumented Test !$#   !2: RobolectricEspresso " & p Instrumented Test !$#  p Espresso*,*(OSS)+) p Robolectric&, )% $"# '(/.  

Slide 38

Slide 38 text

:    38 UiController   loopMainThreadUntilIdle()  p      META-INF/services/ androidx.test.platform.ui.UiController p Espresso : UiControllerImpl p Robolectric : LocalUiController

Slide 39

Slide 39 text

: Robolectric  39 LocalUiController.loopMainThreadUntilIdle() @Override public void loopMainThreadUntilIdle() { shadowMainLooper().idle(); }      

Slide 40

Slide 40 text

: Espresso  40 UiControllerImpl.loopMainThreadUntilIdle() ... do { if (!asyncIdle.isIdleNow()) { ... } if (!compatIdle.isIdleNow()) { ... } if (!dynamicIdle.isIdleNow()) { ... } dynamicIdle = loopUntil(..., dynamicIdle); } while (!asyncIdle.isIdleNow() || !compatIdle.isIdleNow() || !dynamicIdle.isIdleNow());

Slide 41

Slide 41 text

 : Espresso   41 ... do { if (!asyncIdle.isIdleNow()) { ... } if (!compatIdle.isIdleNow()) { ... } if (!dynamicIdle.isIdleNow()) { ... } dynamicIdle = loopUntil(..., dynamicIdle); } while (!asyncIdle.isIdleNow() || !compatIdle.isIdleNow() || !dynamicIdle.isIdleNow());     UiControllerImpl.loopMainThreadUntilIdle()

Slide 42

Slide 42 text

:  loopMain...  42 class IdlingLocalUiController extends LocalUiController { ... public void loopMainThreadUntilIdle() { ... do { ... if (!dynamicIdle.isIdleNow()) { ... } dynamicIdle = loopUntil(..., dynamicIdle); ShadowLooper.shadowMainLooper().idle(); } while (!dynamicIdle.isIdleNow()); } }

Slide 43

Slide 43 text

:  loopMain...  43 class IdlingLocalUiController extends LocalUiController { ... public void loopMainThreadUntilIdle() { ... do { ... if (!dynamicIdle.isIdleNow()) { ... } dynamicIdle = loopUntil(..., dynamicIdle); ShadowLooper.shadowMainLooper().idle(); } while (!dynamicIdle.isIdleNow()); } } Robolectric  

Slide 44

Slide 44 text

class IdlingLocalUiController extends LocalUiController { ... public void loopMainThreadUntilIdle() { ... do { ... if (!dynamicIdle.isIdleNow()) { ... } dynamicIdle = loopUntil(..., dynamicIdle); ShadowLooper.shadowMainLooper().idle(); } while (!dynamicIdle.isIdleNow()); } } :  loopMain...   44 loopMainThreadUntilIdle  

Slide 45

Slide 45 text

class IdlingLocalUiController extends LocalUiController { ... public void loopMainThreadUntilIdle() { ... do { ... if (!dynamicIdle.isIdleNow()) { ... } dynamicIdle = loopUntil(..., dynamicIdle); ShadowLooper.shadowMainLooper().idle(); } while (!dynamicIdle.isIdleNow()); } }  :  loopMain...   45 Espresso dynamicIdle 

Slide 46

Slide 46 text

class IdlingLocalUiController extends LocalUiController { ... public void loopMainThreadUntilIdle() { ... do { ... if (!dynamicIdle.isIdleNow()) { ... } dynamicIdle = loopUntil(..., dynamicIdle); ShadowLooper.shadowMainLooper().idle(); } while (!dynamicIdle.isIdleNow()); } } : loopMain... 46 1

Slide 47

Slide 47 text

:     47 robolectric-4.3.1.jar  META-INF/services/ androidx.test.platform.ui.UiController - org.robolectric.android.internal.LocalUiController + androidx.test.espresso.base.IdlingLocalUiController

Slide 48

Slide 48 text

- org.robolectric.android.internal.LocalUiController + androidx.test.espresso.base.IdlingLocalUiController : . 48 robolectric-4.3.1.jar  META-INF/services/ androidx.test.platform.ui.UiController .

Slide 49

Slide 49 text

 :    49 - final Message m = getNextMessage(); + Duration nextScheduledTaskTime = + shadowOf(Looper.myLooper()) + .getNextScheduledTaskTime(); ... - m.getTarget().dispatchMessage(m); + Thread.sleep(nextScheduledTaskTime.toMillis()); + shadowOf(Looper.myLooper()).runToNextTask();

Slide 50

Slide 50 text

 :    50 - final Message m = getNextMessage(); + Duration nextScheduledTaskTime = + shadowOf(Looper.myLooper()) + .getNextScheduledTaskTime(); ... - m.getTarget().dispatchMessage(m); + Thread.sleep(nextScheduledTaskTime.toMillis()); + shadowOf(Looper.myLooper()).runToNextTask();   

Slide 51

Slide 51 text

(: 51 - final Message m = getNextMessage(); + Duration nextScheduledTaskTime = + shadowOf(Looper.myLooper()) + .getNextScheduledTaskTime(); ... - m.getTarget().dispatchMessage(m); + Thread.sleep(nextScheduledTaskTime.toMillis()); + shadowOf(Looper.myLooper()).runToNextTask(); ) 

Slide 52

Slide 52 text

: ) 52 - final Message m = getNextMessage(); + Duration nextScheduledTaskTime = + shadowOf(Looper.myLooper()) + .getNextScheduledTaskTime(); ... - m.getTarget().dispatchMessage(m); + Thread.sleep(nextScheduledTaskTime.toMillis()); + shadowOf(Looper.myLooper()).runToNextTask(); )   (

Slide 53

Slide 53 text

5 %: (1)+9 */.- 53  sleep ( )   p (4!# %    $:  3"!#  1.  2,78 Robolectric 603-  2. Espresso 5 %  ( $603) 3. " AppNotIdleException &'

Slide 54

Slide 54 text

A.": /?04O  54  E8 https://git.io/Jv2ej >K (commit B" <) (1)Robolectric*9Pull Request:2)  MD04,36@-J   LH http://bit.ly/2TSsKxQ #>K  +;URL/?04N 5I%(&CG *&($'=F!0457

Slide 55

Slide 55 text

+: * !# 55 p https://git.io/Jv2ej ,-% p & $' .  README" IdlingResource ) Robolectric*   (/ ( http://bit.ly/2IKBGPD )

Slide 56

Slide 56 text

0 # : /+ 56 p Instrumented Test),  IdlingResource   p AsyncTask&. %#: ShadowPausedAsyncTask.overrideExecutor IdlingThreadPoolExecutor*' p UI AutomatorUiDevice.wait()  1$!" -(

Slide 57

Slide 57 text

8' :  57 p RobolectricEspresso3 $&% #4< 8'  p Robolectric/9  IdlingResource(671 p ;+):-*0 https://git.io/Jv2ej 5= p Instrumented Test.2 IdlingResource3 Espresso!"#, p AsyncTask(671

Slide 58

Slide 58 text

%) 58 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 59

Slide 59 text

Jetpack:  59 Robolectric UI   Robolectric ↓ Android Architecture Component 20197 by Nozomi Takuma @ Android Test Night #7 (2019/07/25) http://bit.ly/32YCauI

Slide 60

Slide 60 text

Jetpack: JetpackRobolectric   60  Jetpack   p LiveData, Room, WorkManager, etc.  Robolectric      p Room, WorkManager

Slide 61

Slide 61 text

Jetpack:    61 p RobolectricIdlingResource   Instrumented Test   p 2 p TaskExecutorWithIdlingResourceRule p DataBindingIdlingResource

Slide 62

Slide 62 text

Jetpack :    62 TaskExecutorWithIdlingResourceRule https://git.io/Jv2Ud p ArchTaskExecutor(AAC )   p    @get:Rule val executorRule = TaskExecutorWithIdlingResourceRule()

Slide 63

Slide 63 text

Jetpack :   -1 63 DataBindingIdlingResource https://git.io/Jv2UF p Data Binding   (Fragment) p DataBinding  

Slide 64

Slide 64 text

Jetpack :    -2 64 val dbIdlingResource = DataBindingIdlingResource() @Before fun setUp() { val scenario = activityScenarioRule.scenario dbIdlingResource.monitorActivity(scenario) IdlingRegistry.getInstance() .register(dbIndlingResource) } @After fun tearDown() { IdlingRegistry.getInstance() .unregister(dbIndlingResource) }

Slide 65

Slide 65 text

Jetpack:  -3 65 val dbIdlingResource = DataBindingIdlingResource() @Before fun setUp() { val scenario = activityScenarioRule.scenario dbIdlingResource.monitorActivity(scenario) IdlingRegistry.getInstance() .register(dbIndlingResource) } @After fun tearDown() { IdlingRegistry.getInstance() .unregister(dbIndlingResource) }     

Slide 66

Slide 66 text

Jetpack: $!&-4 66 val dbIdlingResource = DataBindingIdlingResource() @Before fun setUp() { val scenario = activityScenarioRule.scenario dbIdlingResource.monitorActivity(scenario) IdlingRegistry.getInstance() .register(dbIndlingResource) } @After fun tearDown() { IdlingRegistry.getInstance() .unregister(dbIndlingResource) }   # "%

Slide 67

Slide 67 text

Jetpack: Robolectric / 67   OK   DataBinding   LifeCycle   LiveData   Navigation (NavigationView )  Paging (  )  Room   ViewModel   WorkManager   SavedState  

Slide 68

Slide 68 text

Jetpack: Robolectric /  68   OK   DataBinding   LifeCycle   LiveData   Navigation (NavigationView )  Paging (  )  Room   ViewModel   WorkManager   SavedState      

Slide 69

Slide 69 text

Jetpack: Room   69 : Robolectric D p  DB : p  static R p  Room.databaseBuilder() : p DBDAO  : 

Slide 70

Slide 70 text

Jetpack : Room (Android Sunflower) 70 @Database(...) abstract class AppDatabase : RoomDatabase() { abstract fun plantDao(): PlantDao companion object { @Volatile private var instance: AppDatabase? = null fun getInstance(ctx: Context): AppDatabase { return instance ?: synchronized(this) { instance ?: buildDatabase(ctx).also { instance = it } } }  

Slide 71

Slide 71 text

Jetpack: Room (Android Sunflower) 71 // companion object private fun buildDatabase(ctx: Context) = Room .databaseBuilder(ctx, AppDatabase::class.java, DATABASE_NAME)...build() @VisibleForTesting(otherwise = NONE) fun clear() { instance = null } ...

Slide 72

Slide 72 text

Jetpack : Room (Android Sunflower) 72 // companion object private fun buildDatabase(ctx: Context) = Room .databaseBuilder(ctx, AppDatabase::class.java, DATABASE_NAME)...build() @VisibleForTesting(otherwise = NONE) fun clear() { instance = null } ...   )( )

Slide 73

Slide 73 text

Jetpack : Room (Android Sunflower) 73 // B A class TestApplication : Application() { override fun onCreate() { ... val db = AppDatabase.getInstance(this) PlantRepository.updateDao(db.plantDao()) ... } } P D P O ) (

Slide 74

Slide 74 text

Jetpack: Room(Android Sunflower) 74 //  @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @Config(application = TestApplication::class) class RobolectricGardenActivityTest2 { ... @After fun tearDown() { val appDatabase = AppDatabase.getInstance( ApplicationProvider.getApplicationContext()) appDatabase.close() AppDatabase.clear() } }

Slide 75

Slide 75 text

Jetpack: Room!(Android Sunflower)# 75 //   @RunWith(AndroidJUnit4::class) @LooperMode(LooperMode.Mode.PAUSED) @Config(application = TestApplication::class) class RobolectricGardenActivityTest2 { ... @After fun tearDown() { val appDatabase = AppDatabase.getInstance( ApplicationProvider.getApplicationContext()) appDatabase.close() AppDatabase.clear() } }    "    

Slide 76

Slide 76 text

Jetpack".: WorkManager".  )0 76 ): WorkManager   p 345Work% (& p static-&/,6(! #!: ' "$ p  '%$21  (http://bit.ly/332dPUP) src/test/AndroidManifest.xml *# 21 p WorkManagerTestInitHelper+ '%$  (http://bit.ly/2VVS5sk)

Slide 77

Slide 77 text

Jetpack : WorkManager  77 // src/test/AndroidManifest.xml  

Slide 78

Slide 78 text

Jetpack#: WorkManager#%) 78 //   class TestApplication : Application() { override fun onCreate() { ... WorkManagerTestInitHelper .initializeTestWorkManager(this) ... } }   • &'(  "  •  ! $

Slide 79

Slide 79 text

Jetpack08:  79 p Jetpack IdlingResource514  p RoomWorkManager:60872 p #!%09)+/3 p     p NavigationView ('( *.&,-)=4 p Paging5 RecyclerView=4 (<,&$",& ; )

Slide 80

Slide 80 text

%) 80 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 81

Slide 81 text

Espresso API:  81 p withId() withText()  $ (!# %) p   click(), replaceText(), typeText(), scrollTo(), pressKey() p "  RecyclerViewActions

Slide 82

Slide 82 text

Espresso API:   82 p DrawerActions openDrawer()    p NavigationViewActions  

Slide 83

Slide 83 text

Espresso API:    83 p ! API2 p DrawerActions p NavigationViewActions p " (   )

Slide 84

Slide 84 text

%) 84 p .+ p Robolectric  p Espresso& Local Test p Espresso- "* ( p ,$'#Android Jetpack Components p ,$'#Espresso API p Robolectric !UI

Slide 85

Slide 85 text

!UI: Robolectric-6 85 p /7(4 .+)   p NavigationView"&$% :0  9 p  #+*:0 # 8(4;, 1235'

Slide 86

Slide 86 text

!UI :   86 p  ' p 1Activity/Fragment, -$A(&B"%   p 1 hop (&.* -$A(&.* B"%   p     )# p Paging RecyclerView + 

Slide 87

Slide 87 text

UI: Shared Test  87 Instrumented TestLocal Test   ? // app/build.gradle sourceSets { androidTest { java.srcDirs += file('src/sharedTest/java') } test { java.srcDirs += file('src/sharedTest/java') } }

Slide 88

Slide 88 text

UI: Shared Test # 88 Instrumented Test Local Test    ? // app/build.gradle sourceSets { androidTest { java.srcDirs += file('src/sharedTest/java') } test { java.srcDirs += file('src/sharedTest/java') } } androidTest test !  src/sharedTest "

Slide 89

Slide 89 text

UI : Shared Test 89   src/sharedTest src/androidTest src/test src/sharedTest Instrumented Test Local Test

Slide 90

Slide 90 text

UI: Shared Test  # 90 p src/sharedTest p AndroidAPIRobolectric"   RobolectricAPI Android!  p EspressoAPI   p UI"src/sharedTest/ PageObject (http://bit.ly/2TzLLVF)  p  () ! (src/androidTest   src/test)

Slide 91

Slide 91 text

!UI:   91 p  "$#.'   p  )! p 1 Activity/Fragment-  p 1 hop *(/,  p Shared Test%& UI   p PageObject+!

Slide 92

Slide 92 text

 92

Slide 93

Slide 93 text

*( 93 Espresso1  ! Robolectric+ 2,87 p Robolectric  static3/DB#$% p IdlingResource1 6 & '4 p JetpackEspresso API0)5-. p  1 p RoomWorkManagerNavigationViewPaging5- p  "

Slide 94

Slide 94 text

URL 94 p Improving Robolectric's Looper simulation http://robolectric.org/blog/2019/06/04/paused-looper/ p Issue #4870 IdlingResource doesn't seem to be working with Robolectric https://github.com/robolectric/robolectric/issues/4807 p Espresso, Beyond the basicsby Iñaki Villar in 360|AnDev 2017 http://bit.ly/2wUb2Bc p Shared Test      by ksfee684 in Cookpad.apk #4 http://bit.ly/2WlQ2Ot

Slide 95

Slide 95 text

3-URL ( 1:=,!() 95 p "2 UI Automator/  in DroidKaigi 2016 ( http://bit.ly/2TP3CYS ) p 2;+Espresso857. in DroidKaigi 2017 ( http://bit.ly/2TzLLVF ) p  Espresso#*6&9 in DroidKaigi 2018 ( http://bit.ly/2QdCeBt ) p EspressoAndroid)%$4  in DroidKaigi 2019 ( http://bit.ly/33lLwRp ) p .<Android'.(0=) https://peaks.cc/sumio_tym/android_testing

Slide 96

Slide 96 text

96    Robolectric  UI