Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Start speking it @ Droidcon Krakow 2017

Rui Gonçalo
December 03, 2017

Start speking it @ Droidcon Krakow 2017

Spek is a fairly new framework that turns your testing code into an enjoyable story to both reader and writer. By writing tests as specifications they become really clean and straightforward, giving the tester confidence. They match perfectly with RxJava code and allow the use of some compelling techniques that makes the usually bored task of writing and reading tests quite entertaining.

Rui Gonçalo

December 03, 2017
Tweet

More Decks by Rui Gonçalo

Other Decks in Programming

Transcript

  1. 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) }
  2. 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() } }
  3. 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() } } }
  4. interface PermissionsManager { fun checkPermission(): Observable<Boolean> fun requestPermissions(): Observable<Boolean> }

    class Presenter( permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { }
  5. interface DistancesProvider { fun getDistances() : Observable<Int> } class Presenter(

    permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { } interface PermissionsManager { fun checkPermission(): Observable<Boolean> fun requestPermissions(): Observable<Boolean> }
  6. 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> }
  7. describe(“check permission”) { context("permission is granted”) { it("should get distances”)

    { verify(distancesProvider).getDistances() }
 }
 } checkPermission GRANT getDistances
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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() } } } } }
  13. 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
  14. 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”) { … } } }
  15. 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”) { … } } }
  16. describe(“check permission”) { context("permission is granted”) { it(“should get distances”)

    { … } } context(“permission is denied”) { it(“should request permission”) { … } } }
  17. 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”) { … } } }
  18. 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”) { … } } }
  19. 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”) { … } } }
  20. describe(“check permission”) { context("permission is granted”) { } context(“permission is

    denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) tested.start() } } }
  21. 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() } } }
  22. 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() } } }
  23. 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() } } }
  24. 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)) } } }
  25. 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)) } } }
  26. 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)) } } }
  27. 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)) } } }
  28. 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”) { } }
  29. 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”) { } }
  30. 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”) { … } } }
  31. 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”) { … } } }
  32. 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”) { … } } }
  33. 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”) { … } } }
  34. 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”) { … } } }
  35. 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”) { … } } }
  36. 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”) { … } } }
  37. 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”) { … } } }
  38. 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”) { … } } } } }
  39. 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() } }
  40. 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() } }
  41. 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() } }
  42. 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() } }
  43. 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) } }
  44. 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) } }