Start Speking It

Start Speking It

This decks is about how we use Spek instead of pure JUnit to test our Android app.

71b4edb678001f55b47bdde0741a0ff5?s=128

Fábio Carballo

January 09, 2018
Tweet

Transcript

  1. Start Speking it. Rui Gonçalo @rmgoncalo Fábio Carballo @fabiocarballo

  2. None
  3. JUnit

  4. None
  5. None
  6. Test Actions Groups

  7. Test • it

  8. Test it(“should be true”) { assertTrue(true) }

  9. Groups • group

  10. Groups group(“a testing group”) { group(“a nested group”) { }

    }
  11. Groups group(“a testing group”) { group(“a nested group”) { }

    }
  12. Groups describe(“a testing group”) { group(“a nested group”) { }

    }
  13. Groups context(“a testing group”) { group(“a nested group”) { }

    }
  14. Groups given(“a testing group”) { group(“a nested group”) { }

    }
  15. 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) }
  16. Actions • on

  17. Actions on(“action that triggers the test”) { tested.actionThatTriggersIt() it(“should do

    X by Y”) { … } }
  18. 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() } }
  19. 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() } } }
  20. None
  21. checkPermission GRANT DENY requestPermission onPermissionGranted onPermissionDenied getDistances getDistances showError

  22. class Presenter( permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { }

  23. interface PermissionsManager { fun checkPermission(): Observable<Boolean> fun requestPermissions(): Observable<Boolean> }

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

    permissionsManager: PermissionsManager, distancesProvider: DistancesProvider, view: View) { } interface PermissionsManager { fun checkPermission(): Observable<Boolean> fun requestPermissions(): Observable<Boolean> }
  25. 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> }
  26. permissionsManager.checkPermission() GRANT DENY permissionsManager.requestPermission() GRANT DENY distancesProvider.getDistances() view.showError() distancesProvider.getDistances()

  27. permissionsManager.checkPermission() GRANT DENY permissionsManager.requestPermission() GRANT DENY distancesProvider.getDistances() view.showError() distancesProvider.getDistances()

  28. checkPermission GRANT getDistances

  29. checkPermission GRANT getDistances @Test fun when_check_permission_and_permission_is_granted_should_get_distance() { … }

  30. @Test fun when_check_permission_and_permission_is_granted_should_get_distance() { whenever(permissionsManager.checkPermission()).thenReturn(Observable.just(true)) } checkPermission GRANT getDistances

  31. @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
  32. checkPermission DENY requestPermission onPermissionGranted getDistances

  33. whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) checkPermission DENY requestPermission onPermissionGranted getDistances

  34. whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Observable.just(true)) checkPermission DENY requestPermission onPermissionGranted getDistances

  35. 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
  36. checkPermission DENY requestPermission onPermissionDenied showError

  37. whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Observable.just(false)) checkPermission DENY requestPermission onPermissionDenied showError

  38. whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) whenever(permissionsManager.requestPermission()) .thenReturn(Observable.just(false)) checkPermission DENY requestPermission onPermissionDenied showError

  39. 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
  40. checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY

    requestPermission onPermissionDenied showError
  41. checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY

    requestPermission onPermissionDenied showError
  42. checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY

    requestPermission onPermissionDenied showError
  43. Spek

  44. checkPermission GRANT getDistances checkPermission DENY requestPermission onPermissionGranted getDistances checkPermission DENY

    requestPermission onPermissionDenied showError
  45. checkPermission GRANT getDistances

  46. describe(“check permission”) { context("permission is granted”) { it("should get distance”)

    { }
 }
 } checkPermission GRANT getDistances
  47. describe(“check permission”) { context("permission is granted”) { it("should get distance”)

    { }
 }
 } checkPermission GRANT getDistances
  48. describe(“check permission”) { context("permission is granted”) { it("should get distances”)

    { }
 }
 } checkPermission GRANT getDistances
  49. describe(“check permission”) { context("permission is granted”) { it("should get distances”)

    { verify(distancesProvider).getDistances() }
 }
 } checkPermission GRANT getDistances
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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() } } } } }
  55. 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
  56. How to setup/teardown ?

  57. beforeEachTest { } afterEachTest { }

  58. 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”) { … } } }
  59. 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”) { … } } }
  60. describe(“check permission”) { context("permission is granted”) { it(“should get distances”)

    { … } } context(“permission is denied”) { it(“should request permission”) { … } } }
  61. 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”) { … } } }
  62. 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”) { … } } }
  63. 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”) { … } } }
  64. describe(“check permission”) { context("permission is granted”) { } context(“permission is

    denied”) { beforeEachTest { whenever(permissionsManager.checkPermission()) .thenReturn(Observable.just(false)) tested.start() } } }
  65. 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() } } }
  66. 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() } } }
  67. 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() } } }
  68. 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)) } } }
  69. 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)) } } }
  70. 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)) } } }
  71. 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)) } } }
  72. 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”) { } }
  73. 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”) { } }
  74. 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”) { … } } }
  75. 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”) { … } } }
  76. 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”) { … } } }
  77. 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”) { … } } }
  78. 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”) { … } } }
  79. 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”) { … } } }
  80. 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”) { … } } }
  81. 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”) { … } } }
  82. 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”) { … } } } } }
  83. None
  84. None
  85. None
  86. Cheat Sheet of Getting into Spek

  87. org.mockito.exceptions.base.MockitoException: Cannot mock/spy class com.package.YourClass Mockito cannot mock/spy because :

    - final class
  88. mock-maker-inline

  89. Mockito.`when`(mapper.call(Mockito.any())).thenReturn(Entity()) fun call(a: A): B

  90. Mockito.any() must not be null java.lang.IllegalStateException: Mockito.any() must not be

    null
  91. Mockito.`when`(mapper.call(Mockito.any())).thenReturn(B()) fun call(a: A): B null

  92. None
  93. 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() } }
  94. 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() } }
  95. 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() } }
  96. 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() } }
  97. 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) } }
  98. 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) } }
  99. None
  100. None
  101. Thank you Rui Gonçalo @rmgoncalo Fábio Carballo @fabiocarballo