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
110
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
870
Improve your tests using Kotlin.
fabiocarballo
1
94
What I've Learned as an Inexperienced Developer
fabiocarballo
0
110
Testing with Kotlin (using Spek and Mockito)
fabiocarballo
0
990
andKotlin
fabiocarballo
0
140
Other Decks in Technology
See All in Technology
Android Audio: Beyond Winning On It
atsushieno
0
3.4k
職種の壁を溶かして開発サイクルを高速に回す~情報透明性と職種越境から考えるAIフレンドリーな職種間連携~
daitasu
0
200
プラットフォーム転換期におけるGitHub Copilot活用〜Coding agentがそれを加速するか〜 / Leveraging GitHub Copilot During Platform Transition Periods
aeonpeople
1
240
ブロックテーマ時代における、テーマの CSS について考える Toro_Unit / 2025.09.13 @ Shinshu WordPress Meetup
torounit
0
130
[ JAWS-UG 東京 CommunityBuilders Night #2 ]SlackとAmazon Q Developerで 運用効率化を模索する
sh_fk2
3
460
まずはマネコンでちゃちゃっと作ってから、それをCDKにしてみよか。
yamada_r
2
120
使いやすいプラットフォームの作り方 ー LINEヤフーのKubernetes基盤に学ぶ理論と実践
lycorptech_jp
PRO
1
170
2つのフロントエンドと状態管理
mixi_engineers
PRO
3
170
Snowflake Intelligence × Document AIで“使いにくいデータ”を“使えるデータ”に
kevinrobot34
1
120
TS-S205_昨年対比2倍以上の機能追加を実現するデータ基盤プロジェクトでのAI活用について
kaz3284
1
230
Rustから学ぶ 非同期処理の仕組み
skanehira
1
150
初めてAWSを使うときのセキュリティ覚書〜初心者支部編〜
cmusudakeisuke
1
280
Featured
See All Featured
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
23
1.4k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
61k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
48
9.7k
Making Projects Easy
brettharned
117
6.4k
Writing Fast Ruby
sferik
628
62k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.1k
Side Projects
sachag
455
43k
BBQ
matthewcrist
89
9.8k
Reflections from 52 weeks, 52 projects
jeffersonlam
352
21k
The Language of Interfaces
destraynor
161
25k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.5k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
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