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
100
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
370
Tackling Android at Scale
fabiocarballo
9
860
Improve your tests using Kotlin.
fabiocarballo
1
91
What I've Learned as an Inexperienced Developer
fabiocarballo
0
110
Testing with Kotlin (using Spek and Mockito)
fabiocarballo
0
980
andKotlin
fabiocarballo
0
140
Other Decks in Technology
See All in Technology
モバイルゲームの開発を支える基盤の歩み ~再現性のある開発ラインを量産する秘訣~
qualiarts
0
1k
クマ×共生 HACKATHON - 熊対策を『特別な行動」から「生活の一部」に -
pharaohkj
0
280
alecthomas/kong はいいぞ
fujiwara3
6
1.3k
Wasmで社内ツールを作って配布しよう
askua
0
180
大規模イベントを支える ABEMA の アーキテクチャ 変遷 2025
nagapad
6
620
Mambaで物体検出 完全に理解した
shirarei24
2
180
AIエージェントを支える設計
tkikuchi1002
12
2.9k
「AI駆動開発」のボトルネック『言語化』を効率化するには
taniiicom
1
240
みんなのSRE 〜チーム全員でのSRE活動にするための4つの取り組み〜
kakehashi
PRO
2
120
ecspressoの設計思想に至る道 / sekkeinight2025
fujiwara3
12
2.3k
LLMでAI-OCR、実際どうなの? / llm_ai_ocr_layerx_bet_ai_day_lt
sbrf248
0
410
マルチモーダル基盤モデルに基づく動画と音の解析技術
lycorptech_jp
PRO
4
410
Featured
See All Featured
A Modern Web Designer's Workflow
chriscoyier
695
190k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.9k
Into the Great Unknown - MozCon
thekraken
40
1.9k
Building Adaptive Systems
keathley
43
2.7k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Fireside Chat
paigeccino
37
3.6k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
What’s in a name? Adding method to the madness
productmarketing
PRO
23
3.6k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
29
9.6k
For a Future-Friendly Web
brad_frost
179
9.8k
KATA
mclloyd
31
14k
Measuring & Analyzing Core Web Vitals
bluesmoon
7
530
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