Slide 1

Slide 1 text

Testing with Spek Fábio Carballo @fabiocarballo

Slide 2

Slide 2 text

Testing with Spek Kotlin Fábio Carballo @fabiocarballo

Slide 3

Slide 3 text

Testing With Kotlin Why do we need tests?

Slide 4

Slide 4 text

“More than the act of testing, the act of designing tests is one of the best bug preventers known.” – Boris Beizer Testing With Kotlin Why do we need tests?

Slide 5

Slide 5 text

“Software testing proves the existing of bugs, not their absence.” – Edsger W. Dijkstra Testing With Kotlin Why do we need tests?

Slide 6

Slide 6 text

“Tests are no more and no less than executable documentation. When writing new code, it makes sense that it should be documented by the person closest to it: the author. That’s my rationale for developer testing. – Noah Sussman Testing With Kotlin Why do we need tests?

Slide 7

Slide 7 text

● J-Unit tests have a flat structure ● RSpec ● TDD test framework for Kotlin ● www.github.com/jetbrains/Spek https://github.com/JetBrains/spekhttps://github.com/JetBrains/spek Testing With Kotlin Why Spek?

Slide 8

Slide 8 text

● Two-player game used to reach a decision. ● One player guesses the number will be even, the other guesses it will be odd. ● The two players pick a number and the winner will be the one that guessed the parity of the sum of those numbers Testing With Kotlin Spek Demo - “Par ou Ímpar” Game

Slide 9

Slide 9 text

Testing With Kotlin Spek Demo - Odd/Even Game enum class Player { EVEN, ODD }

Slide 10

Slide 10 text

Testing With Kotlin Spek Demo - Odd/Even Game open class OddEvenGame() { private var evenPlayerNumber: Int? = null private var oddPlayerNumber: Int? = null fun recordChoice(player: Player, number: Int) { when (player) { Player.EVEN -> evenPlayerNumber = number Player.ODD -> oddPlayerNumber = number } } } enum class Player { EVEN, ODD }

Slide 11

Slide 11 text

Testing With Kotlin Spek Demo - Odd/Even Game open class OddEvenGame() { private var evenPlayerNumber: Int? = null private var oddPlayerNumber: Int? = null fun recordChoice(player: Player, number: Int) { when (player) { Player.EVEN -> evenPlayerNumber = number Player.ODD -> oddPlayerNumber = number } } } enum class Result { EVEN_PLAYER, ODD_PLAYER, NO_RESULT } enum class Player { EVEN, ODD }

Slide 12

Slide 12 text

Testing With Kotlin Spek Demo - Odd/Even Game open class OddEvenGame() { private var evenPlayerNumber: Int? = null private var oddPlayerNumber: Int? = null fun recordChoice(player: Player, number: Int) { when (player) { Player.EVEN -> evenPlayerNumber = number Player.ODD -> oddPlayerNumber = number } } val winner: Result get() { return ifNotNull(evenPlayerNumber, oddPlayerNumber) { evenNumber, oddNumber -> val isEven = ((evenNumber + oddNumber) % 2 == 0) if (isEven) Result.EVEN_PLAYER else Result.ODD_PLAYER } ?: Result.NO_RESULT } } enum class Result { EVEN_PLAYER, ODD_PLAYER, NO_RESULT } enum class Player { EVEN, ODD }

Slide 13

Slide 13 text

Testing With Kotlin open class OddEvenGame() { private var evenPlayerNumber: Int? = null private var oddPlayerNumber: Int? = null fun recordChoice(player: Player, number: Int) { when (player) { Player.EVEN -> evenPlayerNumber = number Player.ODD -> oddPlayerNumber = number } } val winner: Result get() { return ifNotNull(evenPlayerNumber, oddPlayerNumber) { evenNumber, oddNumber -> val isEven = ((evenNumber + oddNumber) % 2 == 0) if (isEven) Result.EVEN_PLAYER else Result.ODD_PLAYER } ?: Result.NO_RESULT } fun clear() { evenPlayerNumber = null oddPlayerNumber = null } } enum class Result { EVEN_PLAYER, ODD_PLAYER, NO_RESULT } enum class Player { EVEN, ODD } Spek Demo - “Par ou Ímpar” Game

Slide 14

Slide 14 text

● What would happen in JUnit? Testing With Kotlin class DemoJunitTest { var oddEvenGame = OddEvenGame(); @Test fun “----------------------------------------------------”() { // Arrange oddEvenGame.recordChoice(Player.EVEN, 4) oddEvenGame.recordChoice(Player.ODD, 3) } @After fun teardown() { oddEvenGame.clear() } } Spek Demo - “Par ou Ímpar” Game

Slide 15

Slide 15 text

● What would happen in JUnit? Testing With Kotlin class DemoJunitTest { var oddEvenGame = OddEvenGame(); @Test fun “----------------------------------------------------”() { // Arrange oddEvenGame.recordChoice(Player.EVEN, 4) oddEvenGame.recordChoice(Player.ODD, 3) // Act val winner = oddEvenGame.winner } @After fun teardown() { oddEvenGame.clear() } } Spek Demo - “Par ou Ímpar” Game

Slide 16

Slide 16 text

● What would happen in JUnit? Testing With Kotlin class DemoJunitTest { var oddEvenGame = OddEvenGame(); @Test fun “----------------------------------------------------”() { // Arrange oddEvenGame.recordChoice(Player.EVEN, 4) oddEvenGame.recordChoice(Player.ODD, 3) // Act val winner = oddEvenGame.winner // Assert Assert.assertEquals(Result.ODD_PLAYER, winner) } @After fun teardown() { oddEvenGame.clear() } } Spek Demo - “Par ou Ímpar” Game

Slide 17

Slide 17 text

● What would happen in JUnit? Testing With Kotlin class DemoJunitTest { var oddEvenGame = OddEvenGame(); @Test fun if_player_odd_chooses_odd_number_and_player_even_chooses_even_number_then_the_winner_is_player_odd() { // Arrange oddEvenGame.recordChoice(Player.EVEN, 4) oddEvenGame.recordChoice(Player.ODD, 3) // Act val winner = oddEvenGame.winner // Assert Assert.assertEquals(Result.ODD_PLAYER, winner) } @After fun teardown() { oddEvenGame.clear() } } Spek Demo - “Par ou Ímpar” Game

Slide 18

Slide 18 text

● What would happen in JUnit? Testing With Kotlin class DemoJunitTest { var oddEvenGame = OddEvenGame(); @Test fun if_player_odd_chooses_odd_number_and_player_even_chooses_even_number_then_the_winner_is_player_odd() { // Arrange oddEvenGame.recordChoice(Player.EVEN, 4) oddEvenGame.recordChoice(Player.ODD, 3) // Act val winner = oddEvenGame.winner // Assert Assert.assertEquals(Result.ODD_PLAYER, winner) } @After fun teardown() { oddEvenGame.clear() } } Spek Demo - “Par ou Ímpar” Game Totally readable!

Slide 19

Slide 19 text

● What would happen in JUnit? Testing With Kotlin Spek Demo - “Par ou Ímpar” Game

Slide 20

Slide 20 text

@RunWith(JUnitPlatform::class) class GameSpek : Spek({ describe("playing odd-even") { val oddEvenGame = OddEvenGame() afterEach { oddEvenGame.clear() } context("when odd player picks an odd number") { beforeEach { oddEvenGame.recordChoice(Player.ODD, 3) } context("when even player picks a even number") { beforeEach { oddEvenGame.recordChoice(Player.EVEN, 4) } it("should declare the odd player as the winner") { assertEquals(Result.ODD_PLAYER, oddEvenGame.winner) } } context("when even player picks an odd number") { beforeEach { oddEvenGame.recordChoice(Player.EVEN, 3) } it("should declare the even player as the winner") { assertEquals(Result.EVEN_PLAYER, oddEvenGame.winner) } } (...) } Testing With Kotlin Spek Demo - “Par ou Ímpar” Game

Slide 21

Slide 21 text

@RunWith(JUnitPlatform::class) class GameSpek : Spek({ describe("playing odd-even") { val oddEvenGame = OddEvenGame() afterEach { oddEvenGame.clear() } context("odd player picks an odd number") { beforeEach { oddEvenGame.recordChoice(Player.ODD, 3) } context("even player picks a even number") { beforeEach { oddEvenGame.recordChoice(Player.EVEN, 4) } it("should declare the odd player as the winner") { assertEquals(Result.ODD_PLAYER, oddEvenGame.winner) } } } } context("when even player picks an odd number") { assertEquals(Result.EVEN_PLAYER, oddEvenGame.winner) } } (...) Testing With Kotlin Spek Demo - “Par ou Ímpar” Game

Slide 22

Slide 22 text

@RunWith(JUnitPlatform::class) class GameSpek : Spek({ describe("playing odd-even") { val oddEvenGame = OddEvenGame() afterEach { oddEvenGame.clear() } context("odd player picks an odd number") { beforeEach { oddEvenGame.recordChoice(Player.ODD, 3) } context("even player picks a even number") { beforeEach { oddEvenGame.recordChoice(Player.EVEN, 4) } it("should declare the odd player as the winner") { assertEquals(Result.ODD_PLAYER, oddEvenGame.winner) } } context("even player picks an odd number") { beforeEach { oddEvenGame.recordChoice(Player.EVEN, 3) } it("should declare the even player as the winner") { assertEquals(Result.EVEN_PLAYER, oddEvenGame.winner) } } } (...) } Testing With Kotlin Spek Demo - “Par ou Ímpar” Game

Slide 23

Slide 23 text

@RunWith(JUnitPlatform::class) class GameSpek : Spek({ describe("playing odd-even") { val oddEvenGame = OddEvenGame() afterEach { oddEvenGame.clear() } context("odd player picks an odd number") { beforeEach { oddEvenGame.recordChoice(Player.ODD, 3) } context("even player picks a even number") { beforeEach { oddEvenGame.recordChoice(Player.EVEN, 4) } it("should declare the odd player as the winner") { assertEquals(Result.ODD_PLAYER, oddEvenGame.winner) } } context("even player picks an odd number") { beforeEach { oddEvenGame.recordChoice(Player.EVEN, 3) } it("should declare the even player as the winner") { assertEquals(Result.EVEN_PLAYER, oddEvenGame.winner) } } } (...) } Testing With Kotlin Spek Demo - “Par ou Ímpar” Game

Slide 24

Slide 24 text

Testing With Kotlin Spek - Subject and Shared Examples The Cat! ● When it moves, it gets hungry. ● When it eats, it stops being hungry

Slide 25

Slide 25 text

Testing With Kotlin Spek - Subject and Shared Examples open class Cat { var isHungry = false open fun eat() { isHungry = false } open fun move() { isHungry = true } }

Slide 26

Slide 26 text

Testing With Kotlin Spek - Subject and Shared Examples The Fat Cat! ● When it moves, it gets hungry. ● When eating for the first time, remains hungry. ● When eating for the second time, stops being hungry

Slide 27

Slide 27 text

Testing With Kotlin Spek - Subject and Shared Examples class FatCat : Cat() { private var ateOnce = false override fun eat() { if (!ateOnce) { ateOnce = true } else { isHungry = false } } override fun move() { ateOnce = false super.move() } }

Slide 28

Slide 28 text

Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class) class MovingCatSpek : SubjectSpek({ subject { Cat() } given("a Cat") { describe("#move") { context("when it is hungry") { beforeEach { subject.isHungry = true } it("should remain hungry") { subject.move() assertTrue(subject.isHungry) } } context("when it is not hungry") { beforeEach { subject.isHungry = false } it("should become hungry") { subject.move() assertTrue(subject.isHungry) } } } } })

Slide 29

Slide 29 text

Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class) class MovingCatSpek : SubjectSpek({ subject { Cat() } given("a Cat") { describe("#move") { context("when it is hungry") { beforeEach { subject.isHungry = true } it("should remain hungry") { subject.move() assertTrue(subject.isHungry) } } context("when it is not hungry") { beforeEach { subject.isHungry = false } it("should become hungry") { subject.move() assertTrue(subject.isHungry) } } } } }) Subject under test!

Slide 30

Slide 30 text

Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class) class FatCatSpek : SubjectSpek({ subject { FatCat() } given("a Fat Cat") { context("when is hungry") { beforeEach { subject.isHungry = true } context("eats for the first time") { beforeEach { subject.eat() } it("remains hungry") { assertTrue(subject.isHungry) } context("eats for the second time") { beforeEach { subject.eat() } it("becomes not hungry") { assertFalse(subject.isHungry) } } } } } })

Slide 31

Slide 31 text

Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class) class FatCatSpek : SubjectSpek({ subject { FatCat() } itBehavesLike(MovingCatSpek::class) given("a Fat Cat") { context("when is hungry") { beforeEach { subject.isHungry = true } context("eats for the first time") { beforeEach { subject.eat() } it("remains hungry") { assertTrue(subject.isHungry) } context("eats for the second time") { beforeEach { subject.eat() } it("becomes not hungry") { assertFalse(subject.isHungry) } } } } } }) Sharing behaviour!!

Slide 32

Slide 32 text

Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class) class FatCatSpek : SubjectSpek({ subject { FatCat() } itBehavesLike(MovingCatSpek::class) given("a Fat Cat") { context("when is hungry") { beforeEach { subject.isHungry = true } context("eats for the first time") { beforeEach { subject.eat() } it("remains hungry") { assertTrue(subject.isHungry) } context("eats for the second time") { beforeEach { subject.eat() } it("becomes not hungry") { assertFalse(subject.isHungry) } } } } } })

Slide 33

Slide 33 text

https://github.com/nhaarman/mockito-kotlin Testing With Kotlin Mockito

Slide 34

Slide 34 text

● https://github.com/nhaarman/mockito-kotlin Testing With Kotlin Mockito interface WebService { fun isOffline(): Boolean fun login(userEmail: String, password: String) fun logout() } data class UserInteractor( private val webService: WebService, private val email: String, private val password: String) { fun login() { if (!webService.isOffline()) { webService.login(email, password) } } fun logout() { if (!webService.isOffline()) { webService.logout() } } }

Slide 35

Slide 35 text

● Creating a mock Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD)

Slide 36

Slide 36 text

● Verify interactions Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.logout()

Slide 37

Slide 37 text

● Verify interactions Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.logout() verify(mockWebService).logout()

Slide 38

Slide 38 text

● Verify interactions Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.logout() verify(mockWebService, times(1)).logout() times(2) times(...) never()

Slide 39

Slide 39 text

● Verify interactions Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.login() verify(mockWebService).login(USER_EMAIL, PASSWORD)

Slide 40

Slide 40 text

● Verify interactions Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.login() verify(mockWebService).login(USER_EMAIL, PASSWORD)

Slide 41

Slide 41 text

● Matchers Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.login() verify(mockWebService).login(any(), any())

Slide 42

Slide 42 text

● In-Order ● Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.login() userInteractor.logout()

Slide 43

Slide 43 text

● In-Order Testing With Kotlin Mockito val mockWebService: WebService = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.login() userInteractor.logout() inOrder(mockWebService).apply { verify(mockWebService).login(USER_EMAIL, PASSWORD) verify(mockWebService).logout() }

Slide 44

Slide 44 text

● Stubbing methods Testing With Kotlin Mockito whenever(mockWebService.isOffline()).thenReturn(false) userInteractor.login() verify(mockWebService, never()).login(any(), any())

Slide 45

Slide 45 text

● Stubbing methods Testing With Kotlin Mockito whenever(mockWebService.isOffline()).thenReturn(false, true, false) userInteractor.login() verify(mockWebService, never()).login(any(), any())

Slide 46

Slide 46 text

● Unable to mock: ○ Private methods ○ Hash Code and Equals ○ Static methods ○ Final classes ○ Final methods https://github.com/dpreussler/kotlin-testrunner https://github.com/dpreussler/kotlin-testrunner Testing With Kotlin Mockito

Slide 47

Slide 47 text

● Unable to mock: ○ Private methods ○ Hash Code and Equals ○ Static methods ○ Final classes ○ Final methods https://github.com/dpreussler/kotlin-testrunner https://github.com/dpreussler/kotlin-testrunner Testing With Kotlin Mockito https://github.com/dpreussler/kotlin-testrunner

Slide 48

Slide 48 text

Testing With Kotlin Simple Android Example What about a simple Android example? https://github.com/fabiocarballo/gdg-lisbon-odd-even

Slide 49

Slide 49 text

Testing With Kotlin Simple Android Example

Slide 50

Slide 50 text

Testing With Kotlin Simple Android Example ● “Par ou Ímpar” game! ○ View - GameActivity ○ Presenter - GamePresenter ○ Model - OddEvenGame

Slide 51

Slide 51 text

Any questions? Testing With Kotlin Q&A