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

Improve your tests using Kotlin.

Improve your tests using Kotlin.

Avatar for Fábio Carballo

Fábio Carballo

October 12, 2017
Tweet

More Decks by Fábio Carballo

Other Decks in Technology

Transcript

  1. • Immutability first • Hides Nullability • Allows extensions to

    third-party classes • High-order functions / Lambdas
  2. class Greeter( private val user: User) { fun getEnglishGreeting() =

    "Hello, ${user.fullName()}!" } class User( private val firstName: String, private val lastName: String) { fun fullName(): String = "$firstName $lastName" }
  3. class GreeterTest { @Mock lateinit var user: User lateinit var

    tested: Greeter @Before fun setUp() { MockitoAnnotations.initMocks(this) tested = Greeter(user) } @Test fun englishGreetIsCorrect() { Mockito.`when`(user.fullName()).thenReturn("Fábio Carballo") assertEquals("Hello, Fábio Carballo!", tested.getEnglishGreeting()) } }
  4. class GreeterTest { @Mock lateinit var user: User lateinit var

    tested: Greeter @Before fun setUp() { MockitoAnnotations.initMocks(this) tested = Greeter(user) } @Test fun englishGreetIsCorrect() { Mockito.`when`(user.fullName()).thenReturn("Fábio Carballo") assertEquals("Hello, Fábio Carballo!", tested.getEnglishGreeting()) } } org.mockito.exceptions.base.MockitoException: Cannot mock/spy class fabiocarballo.com.myapplication.User Mockito cannot mock/spy because : - final class
  5. • “Open” Classes open class User( private val firstName: String,

    private val lastName: String) { open fun fullName(): String = "$firstName $lastName" }
  6. Mockito.verify(user).addFriend(Mockito.any()) class User( private val firstName: String, private val lastName:

    String) { fun addFriend(friend: User) { … } } Mockito Matchers rely on null-values.
  7. Mockito.verify(user).addFriend(Mockito.any()) class User( private val firstName: String, private val lastName:

    String) { fun addFriend(friend: User) { … } } Mockito Matchers rely on null-values. Mockito.verify(user).addFriend(null)
  8. mockito-kotlin val user: User = Mockito.mock(User::class) val user: User =

    mock() val user: User = mock { on { fullName() }.thenReturn(“Fábio Carballo”) }
  9. class GreeterTest { @Mock lateinit var user: User lateinit var

    tested: Greeter @Before fun setUp() { MockitoAnnotations.initMocks(this) tested = Greeter(user) } @Test fun englishGreetIsCorrect() { Mockito.`when`(user.fullName()).thenReturn("Fábio Carballo") assertEquals("Hello, Fábio Carballo!", tested.getEnglishGreeting()) } }
  10. class GreeterTest { val user: User = mock() val tested

    = Greeter(user) @Test fun englishGreetIsCorrect() { whenever(user.fullName()).thenReturn("Fábio Carballo") assertEquals("Hello, Fábio Carballo!", tested.getEnglishGreeting()) } }
  11. class GreeterTest { val user: User = mock() val tested

    = Greeter(user) @Test fun englishGreetIsCorrect() { whenever(user.fullName()).thenReturn("Fábio Carballo") assertEquals("Hello, Fábio Carballo!", tested.getEnglishGreeting()) } } class GreeterTest { val user: User = mock { on { fullName() }.thenReturn("Fábio Carballo") } val tested = Greeter(user) @Test fun englishGreetIsCorrect() { assertEquals("Hello, Fábio Carballo!", tested.getEnglishGreeting()) } }
  12. @Test fun test_addFriend_userHasNoFriends_FriendsListShouldNotBeEmpty() { … } @Test fun `when user

    has no friends and a friend is added the friends list should not be empty`() { … }
  13. class ServiceA { fun methodA() { … } } class

    ServiceB { fun methodB() { … } } class Test { fun test() { val serviceA: ServiceA = mock() val serviceB: ServiceB = mock() serviceA.methodA() serviceB.methodB() val inOrder = inOrder(serviceA, serviceB) inOrder.verify(serviceA).methodA() inOrder.verify(serviceB).methodB() } }
  14. class ServiceA { fun methodA() { … } } class

    ServiceB { fun methodB() { … } } class Test { fun test() { val serviceA: ServiceA = mock() val serviceB: ServiceB = mock() serviceA.methodA() serviceB.methodB() val inOrder = inOrder(serviceA, serviceB) inOrder.verify(serviceA).methodA() inOrder.verify(serviceB).methodB() } }
  15. class ServiceA { fun methodA() { … } } class

    ServiceB { fun methodB() { … } } class Test { fun test() { val serviceA: ServiceA = mock() val serviceB: ServiceB = mock() serviceA.methodA() serviceB.methodB() inOrder(serviceA, serviceB).apply { verify(serviceA).methodA() verify(serviceB).methodB() } } }
  16. data class User( private val firstName: String, private val lastName:

    String, private val age: Int) { val fullName = "$firstName $lastName" val canDrink = (age >= 18) val friends: MutableList<User> = mutableListOf() fun addFriend(user: User) { if (user !in friends) { friends.add(user) user.addFriend(this) } } }
  17. @Test fun `the full name is the concatenation of the

    first and last name`() { val user = User(firstName = "Fábio", lastName = "Carballo", age = 26) assertEquals("Fábio Carballo", user.fullName) }
  18. @Test fun `the full name is the concatenation of the

    first and last name`() { val user = User(firstName = "Fábio", lastName = "Carballo", age = 26) assertEquals("Fábio Carballo", user.fullName) user.fullName `should equal` "Fábio Carballo” // Kluent }
  19. @Test fun `the full name is the concatenation of the

    first and last name`() { val user = User(firstName = "Fábio", lastName = "Carballo", age = 26) assertEquals("Fábio Carballo", user.fullName) user.fullName `should equal` "Fábio Carballo” // Kluent } infix fun Any.`should equal`(theOther: Any) = assertEquals(theOther, this)
  20. @Test fun `the full name is the concatenation of the

    first and last name`() { val user = User(firstName = "Fábio", lastName = "Carballo", age = 26) assertEquals("Fábio Carballo", user.fullName) user.fullName `should equal` "Fábio Carballo” // Kluent user.fullName.should.equal(“Fábio Carballo”) // Expect }
  21. @Test fun `friends are correctly added`() { val rui =

    User(firstName = "Rui", lastName = "Gonçalo", age = 28) val fabio = User(firstName = "Fábio", lastName = "Carballo", age = 26) rui.addFriend(fabio) assertEquals(listOf(fabio), rui.friends) // OR assertTrue { fabio in rui.friends } }
  22. @Test fun `friends are correctly added`() { val rui =

    User(firstName = "Rui", lastName = "Gonçalo", age = 28) val fabio = User(firstName = "Fábio", lastName = "Carballo", age = 26) rui.addFriend(fabio) assertEquals(listOf(fabio), rui.friends) // KLUENT rui.friends `should contain` fabio rui.friends `should not contain` rui }
  23. @Test fun `friends are correctly added`() { val rui =

    User(firstName = "Rui", lastName = "Gonçalo", age = 28) val fabio = User(firstName = "Fábio", lastName = "Carballo", age = 26) rui.addFriend(fabio) assertEquals(listOf(fabio), rui.friends) // KLUENT rui.friends `should contain` fabio rui.friends `should not contain` rui // EXPEKT rui.friends.should.contain(fabio) }
  24. @Test fun `friends are correctly added`() { val rui =

    User(firstName = "Rui", lastName = "Gonçalo", age = 28) val fabio = User(firstName = "Fábio", lastName = "Carballo", age = 26) rui.addFriend(fabio) assertEquals(listOf(fabio), rui.friends) // KLUENT rui.friends `should contain` fabio rui.friends `should not contain` rui // EXPEKT rui.friends.should.contain(fabio) fabio.friends.should.have.all.elements(rui).and.have.size(1) }
  25. @Test fun `friends are correctly added`() { val rui =

    User(firstName = "Rui", lastName = "Gonçalo", age = 28) val fabio = User(firstName = "Fábio", lastName = "Carballo", age = 26) rui.addFriend(fabio) assertEquals(listOf(fabio), rui.friends) // KLUENT rui.friends `should contain` fabio rui.friends `should not contain` rui // EXPEKT rui.friends.should.contain(fabio) fabio.friends.should.have.all.elements(rui).and.have.size(1) expect(rui.friends).to.contain(fabio) }
  26. fun drink() { if (canDrink) { System.out.println("Drinking .. ") }

    else { throw UnderageDrinkingException() } } class UnderageDrinkingException() : Exception("You can't drink.")
  27. fun drink() { if (canDrink) { System.out.println("Drinking .. ") }

    else { throw UnderageDrinkingException() } } class UnderageDrinkingException() : Exception("You can't drink.") @Test(expected = UnderageDrinkingException::class) fun `an underage user can't drink`() { val ricardo = User( firstName = "Ricardo", lastName = "Trindade", age = 17) ricardo.drink() }
  28. fun drink() { if (canDrink) { System.out.println("Drinking .. ") }

    else { throw UnderageDrinkingException() } } class UnderageDrinkingException() : Exception("You can't drink.") fun `an underage user can't drink`() { val ricardo = User( firstName = "Ricardo", lastName = "Trindade", age = 17) val ricardoDrinks = { ricardo.drink() }
 
 ricardoDrinks `should throw` UnderageDrinkingException::class }
  29. fun drink() { if (canDrink) { System.out.println("Drinking .. ") }

    else { throw UnderageDrinkingException() } } class UnderageDrinkingException() : Exception("You can't drink.") fun `an underage user can't drink`() { val ricardo = User( firstName = "Ricardo", lastName = "Trindade", age = 17) val ricardoDrinks = { ricardo.drink() }
 
 ricardoDrinks `should throw the Exception` UnderageDrinkingException::class `with message` "You can't drink." }
  30. Hangman •Player 1 thinks about an arbitrary word. •Other players

    try to guess the word by suggesting a letter. •Each wrong guess, a part of the hangman is drawn.
  31. class Hangman(private val word: String) { var wrongAnswerCount = 0

    fun suggestLetter(letter: Char): Boolean { … } }
  32. @RunWith(JUnitPlatform::class) class HangmanFeatureSpec : Spek({ val game = Hangman("apple") describe("letter

    verification") { it("returns true when the suggestion is correct") { game.suggestLetter('a') `should equal` true } it("returns false when the suggestion is incorrect") { game.suggestLetter('f') `should equal` false } } })
  33. @RunWith(JUnitPlatform::class) class HangmanFeatureSpec : Spek({ val game = Hangman("apple") describe("letter

    verification") { it("returns true when the suggestion is correct") { game.suggestLetter('a') `should equal` true } it("returns false when the suggestion is incorrect") { game.suggestLetter('f') `should equal` false } } })
  34. @RunWith(JUnitPlatform::class) class HangmanBehaviorSpec : Spek({ val hiddenWord = "apple" given("a

    new game with the hidden word being $hiddenWord:") { val game = Hangman(hiddenWord) on(“correct suggestion") { game.suggestLetter(‘a’) it("has no wrong answers") { game.wrongAnswerCount `should equal` 0 } it("shows the guessed letters") { game.dashedWord `should equal` "a----" } } } })
  35. @RunWith(JUnitPlatform::class) class HangmanBehaviorSpec : Spek({ val hiddenWord = "apple" given("a

    new game with the hidden word being $hiddenWord:") { val game = Hangman(hiddenWord) on(“correct suggestion") { game.suggestLetter(‘a’) it("has no wrong answers") { game.wrongAnswerCount `should equal` 0 } it("shows the guessed letters") { game.dashedWord `should equal` "a----" } } } })
  36. fun SpecBody.describe(description: String, body: SpecBody.() -> Unit) { group("describe $description",

    body = body) } 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) }
  37. Ignoring tests • Just add an X as a prefix

    to the scope xit("returns true when the suggestion is correct") { game.suggestLetter('a') `should equal` true }
  38. Ignoring tests • Just add an X as a prefix

    to the scope xit("returns true when the suggestion is correct") { game.suggestLetter('a') `should equal` true }