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

Testing with Kotlin (using Spek and Mockito)

Testing with Kotlin (using Spek and Mockito)

These are the slides that support my talk given at DevFest Lisbon! I talked about how you could test Kotlin code using both Spek and Mockito.

Fábio Carballo

September 24, 2016
Tweet

More Decks by Fábio Carballo

Other Decks in Programming

Transcript

  1. “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?
  2. “Software testing proves the existing of bugs, not their absence.”

    – Edsger W. Dijkstra Testing With Kotlin Why do we need tests?
  3. “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?
  4. • 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?
  5. • 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
  6. 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 }
  7. 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 }
  8. 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 }
  9. 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
  10. • 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
  11. • 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
  12. • 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
  13. • 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
  14. • 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!
  15. @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
  16. @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
  17. @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
  18. @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
  19. Testing With Kotlin Spek - Subject and Shared Examples The

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

    class Cat { var isHungry = false open fun eat() { isHungry = false } open fun move() { isHungry = true } }
  21. 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
  22. 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() } }
  23. Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class)

    class MovingCatSpek : SubjectSpek<Cat>({ 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) } } } } })
  24. Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class)

    class MovingCatSpek : SubjectSpek<Cat>({ 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!
  25. Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class)

    class FatCatSpek : SubjectSpek<FatCat>({ 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) } } } } } })
  26. Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class)

    class FatCatSpek : SubjectSpek<FatCat>({ 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!!
  27. Testing With Kotlin Spek - Subject and Shared Examples @RunWith(JUnitPlatform::class)

    class FatCatSpek : SubjectSpek<FatCat>({ 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) } } } } } })
  28. • 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() } } }
  29. • Creating a mock Testing With Kotlin Mockito val mockWebService:

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

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

    = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.logout() verify(mockWebService).logout()
  32. • 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()
  33. • 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)
  34. • 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)
  35. • Matchers Testing With Kotlin Mockito val mockWebService: WebService =

    mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.login() verify(mockWebService).login(any(), any())
  36. • In-Order • Testing With Kotlin Mockito val mockWebService: WebService

    = mock() val userInteractor = UserInteractor(mockWebService, USER_EMAIL, PASSWORD) userInteractor.login() userInteractor.logout()
  37. • 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() }
  38. • 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
  39. • 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
  40. Testing With Kotlin Simple Android Example What about a simple

    Android example? https://github.com/fabiocarballo/gdg-lisbon-odd-even
  41. Testing With Kotlin Simple Android Example • “Par ou Ímpar”

    game! ◦ View - GameActivity ◦ Presenter - GamePresenter ◦ Model - OddEvenGame