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 JVM5SQLite(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 ModeRobolectric4.3 PAUSED9,
LEGACY
p )&"$#!$-67+=1
p .3 '*/
;
UI
2
PAUSED
p .3 '*/58
p EspressoUI#!$
0>
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
: 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
+: * !#
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
RobolectricUI
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) }
# "%
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)
Jetpack08:
79
p Jetpack
IdlingResource514
p RoomWorkManager:60872
p #!%09)+/3
p
p NavigationView ('( *.&,-)=4
p Paging5RecyclerView=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 PagingRecyclerView +
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 1Activity/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