Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Start Speking It
Search
Fábio Carballo
January 09, 2018
Technology
0
86
Start Speking It
This decks is about how we use Spek instead of pure JUnit to test our Android app.
Fábio Carballo
January 09, 2018
Tweet
Share
More Decks by Fábio Carballo
See All by Fábio Carballo
Kickstarting our Design System
fabiocarballo
2
350
Tackling Android at Scale
fabiocarballo
9
730
Improve your tests using Kotlin.
fabiocarballo
1
78
What I've Learned as an Inexperienced Developer
fabiocarballo
0
100
Testing with Kotlin (using Spek and Mockito)
fabiocarballo
0
910
andKotlin
fabiocarballo
0
120
Other Decks in Technology
See All in Technology
プロダクトエンジニアが活躍する環境を作りたくて 事業責任者になった話 ~プロダクトエンジニアの行き着く先~
gimupop
1
480
Commitment vs Harrisonism - Keynote for Scrum Niseko 2024
miholovesq
6
1.1k
ABEMA のコンテンツ制作を最適化!生成 AI x クラウド映像編集システム / abema-ai-editor
cyberagentdevelopers
PRO
1
180
AWS CDKでデータリストアの運用、どのように設計する?~Aurora・EFSの実践事例を紹介~/aws-cdk-data-restore-aurora-efs
mhrtech
4
650
WINTICKETアプリで実現した高可用性と高速リリースを支えるエコシステム / winticket-eco-system
cyberagentdevelopers
PRO
1
190
生成AIと知識グラフの相互利用に基づく文書解析
koujikozaki
1
140
Forget efficiency – Become more productive without the stress
ufried
0
140
10分でわかるfreeeのQA
freee
1
3.4k
Figma Dev Modeで進化するデザインとエンジニアリングの協働 / figma-with-engineering
cyberagentdevelopers
PRO
1
430
新R25、乃木坂46 Mobileなどのファンビジネスを支えるマルチテナンシーなプラットフォームの全体像 / cam-multi-cloud
cyberagentdevelopers
PRO
1
130
チームを主語にしてみる / Making "Team" the Subject
ar_tama
4
310
AWS re:Inventを徹底的に楽しむためのTips / Tips for thoroughly enjoying AWS re:Invent
yuj1osm
1
570
Featured
See All Featured
Raft: Consensus for Rubyists
vanstee
136
6.6k
The Invisible Side of Design
smashingmag
297
50k
How to train your dragon (web standard)
notwaldorf
88
5.7k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
The Pragmatic Product Professional
lauravandoore
31
6.3k
Teambox: Starting and Learning
jrom
132
8.7k
KATA
mclloyd
29
13k
Learning to Love Humans: Emotional Interface Design
aarron
272
40k
Building Applications with DynamoDB
mza
90
6.1k
Code Review Best Practice
trishagee
64
17k
Happy Clients
brianwarren
97
6.7k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
246
1.3M
Transcript
Start Speking it. Rui Gonçalo @rmgoncalo Fábio Carballo @fabiocarballo
None
JUnit
None
None
Test Actions Groups
Test • it
Test it(“should be true”) { assertTrue(true) }
Groups • group
Groups group(“a testing group”) { group(“a nested group”) { }
}
Groups group(“a testing group”) { group(“a nested group”) { }
}
Groups describe(“a testing group”) { group(“a nested group”) { }
}
Groups context(“a testing group”) { group(“a nested group”) { }
}
Groups given(“a testing group”) { group(“a nested group”) { }
}
Groups fun SpecBody.context(description: String, body: SpecBody.() -> Unit) { group("context
$description", body = body) } fun SpecBody.given(description: String, body: SpecBody.() -> Unit) { group(“given $description", body = body) } fun SpecBody.describe(description: String, body: SpecBody.() -> Unit) { group(“describe $description", body = body) }
Actions • on
Actions on(“action that triggers the test”) { tested.actionThatTriggersIt() it(“should do
X by Y”) { … } }
object SimpleSpec: Spek({ describe("a bakery") { val calculator = SampleCalculator()
on(“bake bread") { val breads = bakery.mix(“salt”, “margarine” , “flour”) it(“should return some bread") { assertTrue(breads.isNotEmpty() } }
object SimpleSpec: Spek({ describe("a bakery") { val bakery = Bakery()
on(“mixing bread ingredients”) { val breads = bakery.mix(“salt”, “margarine”, “flour”) it(“should return some bread") { assertTrue(breads.isNotEmpty() } } }
None
checkPermission GRANT DENY requestPermission onPermissionGranted onPermissionDenied getDistances getDistances showError
class Presenter( permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { }
interface PermissionsManager { fun checkPermission(): Observable<Boolean> fun requestPermissions(): Observable<Boolean> }
class Presenter( permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { }
interface DistancesProvider { fun getDistances() : Observable<Int> } class Presenter(
permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { } interface PermissionsManager { fun checkPermission(): Observable<Boolean> fun requestPermissions(): Observable<Boolean> }
interface View { fun showDistance(distance: Int) fun showError() } interface
PermissionsManager { fun checkPermission(): Observable<Boolean> fun requestPermissions(): Observable<Boolean> } class Presenter( permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { } interface DistancesProvider { fun getDistances() : Observable<Int> }
permissionsManager.checkPermission() GRANT DENY permissionsManager.requestPermission() GRANT DENY distancesProvider.getDistances() view.showError() distancesProvider.getDistances()
permissionsManager.checkPermission() GRANT DENY permissionsManager.requestPermission() GRANT DENY distancesProvider.getDistances() view.showError() distancesProvider.getDistances()
checkPermission GRANT getDistances
checkPermission GRANT getDistances @Test fun when_check_permission_and_permission_is_granted_should_get_distance() { … }
@Test fun when_check_permission_and_permission_is_granted_should_get_distance() { whenever(permissionsManager.checkPermission()).thenReturn(Observable.just(true)) } checkPermission GRANT getDistances
@Test fun when_check_permission_and_permission_is_granted_should_get_distance() { whenever(permissionsManager.checkPermission()).thenReturn(Observable.just(true)) presenter.start() verify(distancesProvider).getDistance() } checkPermission GRANT
getDistances
checkPermission DENY requestPermission onPermissionGranted getDistances
whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) checkPermission DENY requestPermission onPermissionGranted getDistances
whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Observable.just(true)) checkPermission DENY requestPermission onPermissionGranted getDistances
whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Observable.just(true)) presenter.start() verify(permissionsManager).requestPermission() verify(view).getDistances() checkPermission DENY requestPermission
onPermissionGranted getDistances
checkPermission DENY requestPermission onPermissionDenied showError
whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Observable.just(false)) checkPermission DENY requestPermission onPermissionDenied showError
whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Observable.just(false)) checkPermission DENY requestPermission onPermissionDenied showError
whenever(permissionsManager.checkPermission()) .thenReturn(Single.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Single.just(false)) presenter.start() verify(permissionsManager).requestPermission() verify(view).showError() checkPermission DENY requestPermission
onPermissionDenied showError
checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY
requestPermission onPermissionDenied showError
checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY
requestPermission onPermissionDenied showError
checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY
requestPermission onPermissionDenied showError
Spek
checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY
requestPermission onPermissionDenied showError
checkPermission GRANT getDistances
describe(“check permission”) { context("permission is granted”) { it("should get distance”)
{ } } } checkPermission GRANT getDistances
describe(“check permission”) { context("permission is granted”) { it("should get distance”)
{ } } } checkPermission GRANT getDistances
describe(“check permission”) { context("permission is granted”) { it("should get distances”)
{ } } } checkPermission GRANT getDistances
describe(“check permission”) { context("permission is granted”) { it("should get distances”)
{ verify(distancesProvider).getDistances() } } } checkPermission GRANT getDistances
describe(“check permission”) { context("permission is granted”) { it("should get distances”)
{ verify(distancesProvider).getDistances() } } context("permission is denied”) { it(“should request permissions”) { verify(permissionsManager).requestPermissions() } } } checkPermission GRANT getDistances DENY requestPermission
describe(“check permission”) { … context("permission is denied”) { it(“should request
permissions”) { … } given(“a permission request”) { on(“permission granted”) { it(“should get distance”) { … } } } } } checkPermission GRANT getDistances DENY requestPermission onPermissionGranted
describe(“check permission”) { … context("permission is denied”) { it(“should request
permissions”) { … } given(“a permission request”) { on(“permission granted”) { it(“should get distances”) { } } } } } checkPermission GRANT getDistances DENY requestPermission onPermissionGranted getDistances
describe(“check permission”) { … context("permission is denied”) { it(“should request
permissions”) { … } given(“a permission request”) { on(“permission granted”) { it(“should get distances”) { verify(distancesProvider).getDistances() } } } } } checkPermission GRANT getDistances DENY requestPermission onPermissionGranted getDistances
checkPermission GRANT getDistances DENY requestPermission onPermissionGranted getDistances onPermissionDenied showError describe(“check
permission”) { context("permission is denied”) { given(“a permission request”) { on(“permission granted”) { it(“should get distances”) { … } } on(“permission denied”) { it(“should show error”) { verify(view).showError() } } } } }
describe(“check permission”) { context("permission is granted”) it(“should get distances”) {
… } context(“permission is denied”) { it(“should request permission”) { … } given(“a permission request”) { on(“permission granted”) it(“should get distances”) { … } on(“permission denied”) it(“should show error”) { … } } } checkPermission GRANT getDistances DENY requestPermission onPermissionGranted getDistances onPermissionDenied showError
How to setup/teardown ?
beforeEachTest { } afterEachTest { }
describe(“check permission”) { context("permission is granted”) { it(“should get distances”)
{ … } } context(“permission is denied”) { it(“should request permission”) { … } given(“a permission request”) { on(“permission granted”) it(“should get distances”) { … } on(“permission denied”) it(“should show error”) { … } } }
describe(“check permission”) { context("permission is granted”) { it(“should get distances”)
{ … } } context(“permission is denied”) { it(“should request permission”) { … } given(“a permission request”) { on(“permission granted”) it(“should get distances”) { … } on(“permission denied”) it(“should show error”) { … } } }
describe(“check permission”) { context("permission is granted”) { it(“should get distances”)
{ … } } context(“permission is denied”) { it(“should request permission”) { … } } }
describe(“check permission”) { context("permission is granted”) { beforeEachTest { whenever(permissionsManager.checkPermission())
.thenReturn(Observable.just(true)) tested.start() } it(“should get distances”) { … } } context(“permission is denied”) { it(“should request permission”) { … } } }
describe(“check permission”) { context("permission is granted”) { it(“should get distances”)
{ … } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Single.just(false)) tested.start() } it(“should request permission”) { … } } }
describe(“check permission”) { context("permission is granted”) { it(“should get distances”)
{ … } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) tested.start() } it(“should request permission”) { … } } }
describe(“check permission”) { context("permission is granted”) { } context(“permission is
denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) tested.start() } } }
describe(“check permission”) { context("permission is granted”) { beforeEachTest { whenever(permissionsManager.checkPermission())
.thenReturn(Observable.just(true)) tested.start() } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) tested.start() } } }
describe(“check permission”) { context("permission is granted”) { beforeEachTest { whenever(permissionsManager.checkPermission())
.thenReturn(Observable.just(true)) tested.start() } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) tested.start() } } }
describe(“start”) { context("permission is granted”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true))
tested.start() } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) tested.start() } } }
describe(“start”) { beforeEachTest { tested.start() } context("permission is granted”) {
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true)) } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) } } }
describe(“start”) { beforeEachTest { tested.start() } it(“should check permission”) {
verify(permissionsManager).checkPermission() } context("permission is granted”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true)) } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) } } }
describe(“start”) { beforeEachTest { tested.start() } it(“should check permission”) {…}
context("permission is granted”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true)) } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) } } }
describe(“start”) { beforeEachTest { tested.start() } it(“should check permission”) {…}
context("permission is granted”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true)) } } context(“permission is denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) } } }
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true)) } describe(“start”) { beforeEachTest { tested.start()
} it(“should check permission”) {…} context("permission is granted”) { } context(“permission is denied”) { } }
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true)) } describe(“start”) { beforeEachTest { tested.start()
} it(“should check permission”) {…} context("permission is granted”) { } context(“permission is denied”) { } }
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(true)) } describe(“start”) { beforeEachTest { tested.start()
} it(“should check permission”) {…} context("permission is granted”) { } context(“permission is denied”) { it(“should request permission”) { … } } }
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(____________) } describe(“start”) { beforeEachTest { tested.start()
} it(“should check permission”) {…} context("permission is granted”) { } context(“permission is denied”) { it(“should request permission”) { … } } }
val permissionPubSub: PublishSubject<Boolean> = PublishSubject.create() beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(permissionPubSub) }
describe(“start”) { beforeEachTest { tested.start() } it(“should check permission”) {…} context("permission is granted”) { } context(“permission is denied”) { it(“should request permission”) { … } } }
val permissionPubSub: PublishSubject<Boolean> = PublishSubject.create() beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(permissionPubSub) }
describe(“start”) { beforeEachTest { tested.start() } it(“should check permission”) {…} context("permission is granted”) { } context(“permission is denied”) { it(“should request permission”) { … } } }
val permissionPubSub: PublishSubject<Boolean> = PublishSubject.create() beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(permissionPubSub) }
describe(“start”) { beforeEachTest { tested.start() } it(“should check permission”) {…} context("permission is granted”) { } context(“permission is denied”) { beforeEachTest { permissionPubSub.onNext(false) } it(“should request permission”) { … } } }
val permissionPubSub: PublishSubject<Boolean> = PublishSubject.create() beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(permissionPubSub) }
describe(“start”) { beforeEachTest { tested.start() } it(“should check permission”) {…} context("permission is granted”) { beforeEachTest { permissionPubSub.onNext(true) } it(“should get distances”) { … } } context(“permission is denied”) { beforeEachTest { permissionPubSub.onNext(false) } it(“should request permission”) { … } } }
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(permissionPubSub) whenever(permissionsManager.requestPermission()) .thenReturn(requestPubSub) } describe(“start”) { beforeEachTest
{ tested.start() } it(“should check permission”) {…} context("permission is granted”) { beforeEachTest { permissionPubSub.onNext(true) } it(“should get distances”) { … } } context(“permission is denied”) { beforeEachTest { permissionPubSub.onNext(false) } it(“should request permission”) { … } } }
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(permissionPubSub) whenever(permissionsManager.requestPermission()) .thenReturn(requestPubSub) } describe(“start”) { beforeEachTest
{ tested.start() } it(“should check permission”) {…} context("permission is granted”) { beforeEachTest { permissionPubSub.onNext(true) } it(“should get distances”) { … } } context(“permission is denied”) { beforeEachTest { permissionPubSub.onNext(false) } it(“should request permission”) { … } } }
beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(permissionPubSub) whenever(permissionsManager.requestPermission()) .thenReturn(requestPubSub) } describe(“start”) { context(“permission
is denied”) { given(“a permission request”) { on(“permission granted”) { requestPubSub.onNext(true) it(“should get distances”) { … } } on(“permission denied”) { requestPubSub.onNext(false) it(“should show error”) { … } } } } }
None
None
None
Cheat Sheet of Getting into Spek
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class com.package.YourClass Mockito cannot mock/spy because :
- final class
mock-maker-inline
Mockito.`when`(mapper.call(Mockito.any())).thenReturn(Entity()) fun call(a: A): B
Mockito.any() must not be null java.lang.IllegalStateException: Mockito.any() must not be
null
Mockito.`when`(mapper.call(Mockito.any())).thenReturn(B()) fun call(a: A): B null
None
fun SpecBody.rxGiven(description: String, body: SpecBody.() -> Unit) { val immediate
= object : Scheduler() { override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable { return super.scheduleDirect(run, 0, unit) } override fun createWorker(): Scheduler.Worker { return ExecutorScheduler.ExecutorWorker(Runnable::run) } } beforeEachTest { RxJavaPlugins.setInitIoSchedulerHandler{ immediate } RxJavaPlugins.setInitComputationSchedulerHandler{ immediate } RxJavaPlugins.setInitNewThreadSchedulerHandler{ immediate } RxJavaPlugins.setInitSingleSchedulerHandler{ immediate } RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> immediate } } given(description, body = body) afterEachTest { RxJavaPlugins.reset() RxAndroidPlugins.reset() } }
fun SpecBody.rxGiven(description: String, body: SpecBody.() -> Unit) { val immediate
= object : Scheduler() { override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable { return super.scheduleDirect(run, 0, unit) } override fun createWorker(): Scheduler.Worker { return ExecutorScheduler.ExecutorWorker(Runnable::run) } } beforeEachTest { RxJavaPlugins.setInitIoSchedulerHandler{ immediate } RxJavaPlugins.setInitComputationSchedulerHandler{ immediate } RxJavaPlugins.setInitNewThreadSchedulerHandler{ immediate } RxJavaPlugins.setInitSingleSchedulerHandler{ immediate } RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> immediate } } given(description, body = body) afterEachTest { RxJavaPlugins.reset() RxAndroidPlugins.reset() } }
fun SpecBody.rxGiven(description: String, body: SpecBody.() -> Unit) { val immediate
= object : Scheduler() { override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable { return super.scheduleDirect(run, 0, unit) } override fun createWorker(): Scheduler.Worker { return ExecutorScheduler.ExecutorWorker(Runnable::run) } } beforeEachTest { RxJavaPlugins.setInitIoSchedulerHandler{ immediate } RxJavaPlugins.setInitComputationSchedulerHandler{ immediate } RxJavaPlugins.setInitNewThreadSchedulerHandler{ immediate } RxJavaPlugins.setInitSingleSchedulerHandler{ immediate } RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> immediate } } given(description, body = body) afterEachTest { RxJavaPlugins.reset() RxAndroidPlugins.reset() } }
fun SpecBody.rxGiven(description: String, body: SpecBody.() -> Unit) { val immediate
= object : Scheduler() { override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable { return super.scheduleDirect(run, 0, unit) } override fun createWorker(): Scheduler.Worker { return ExecutorScheduler.ExecutorWorker(Runnable::run) } } beforeEachTest { RxJavaPlugins.setInitIoSchedulerHandler{ immediate } RxJavaPlugins.setInitComputationSchedulerHandler{ immediate } RxJavaPlugins.setInitNewThreadSchedulerHandler{ immediate } RxJavaPlugins.setInitSingleSchedulerHandler{ immediate } RxAndroidPlugins.setInitMainThreadSchedulerHandler { scheduler -> immediate } } given(description, body = body) afterEachTest { RxJavaPlugins.reset() RxAndroidPlugins.reset() } }
fun SpecBody.rxGiven(description: String, body: SpecBody.() -> Unit) { beforeEachTest {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) { runnable.run() } override fun postToMainThread(runnable: Runnable) { runnable.run() } override fun isMainThread(): Boolean { return true } }) } given(description, body = body) afterEachTest { ArchTaskExecutor.getInstance().setDelegate(null) } }
fun SpecBody.rxGiven(description: String, body: SpecBody.() -> Unit) { beforeEachTest {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) { runnable.run() } override fun postToMainThread(runnable: Runnable) { runnable.run() } override fun isMainThread(): Boolean { return true } }) } given(description, body = body) afterEachTest { ArchTaskExecutor.getInstance().setDelegate(null) } }
None
None
Thank you Rui Gonçalo @rmgoncalo Fábio Carballo @fabiocarballo