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

Transforming your Tests with Kotlin

Transforming your Tests with Kotlin

We know we should write tests, and we know that Kotlin gives us some nice features to use in our code, so how can we put these two together? There are some really great ways that Kotlin is being used in test writing, and we'll look at a number of them. This will include features such as higher order functions and escaping back-ticks and how they can make your tests more expressive. We'll also look at some testing libraries that take advantage of Kotlin's features. At the end of this talk you'll walk away with an understanding of how you can use Kotlin to make your tests more enjoyable.

Victoria Gonda

June 26, 2019
Tweet

More Decks by Victoria Gonda

Other Decks in Programming

Transcript

  1. Transforming Your Tests With Kotlin Victoria Gonda
    victoriagonda.com
    @TTGonda

    View Slide

  2. Testing with Kotlin

    View Slide

  3. https://twitter.com/TTGonda/status/1123025116139544576

    View Slide

  4. Victoria Gonda
    • Android Engineer at Buffer
    • Author and Editor at RayWenderlich.com
    • Hedgehog and cat owner
    • she/her

    View Slide

  5. Kotlin features

    View Slide

  6. `Backticks`

    View Slide

  7. https://twitter.com/skentphd/status/1123035724788453377 https://twitter.com/jshvarts/status/1123362376840040448

    View Slide

  8. Without `Backticks`
    @Test
    fun whenDeleteIsCalled_onlyCorrespondingDataIsDeleted() {
    }

    View Slide

  9. `Backticks`
    @Test
    fun `deleteUpdates only deletes corresponding data`() {
    }

    View Slide

  10. `Backticks`
    java.lang.AssertionError: Assertion failed at
    package.Test.deleteUpdates only deletes corresponding data

    View Slide

  11. Higher order functions

    View Slide

  12. https://twitter.com/wakingrufus/status/
    1123053243800653825
    https://twitter.com/RBusarow/status/
    1123057633261441024
    https://twitter.com/jessewilson/status/
    1123055645824028672

    View Slide

  13. Higher order functions
    fun verifyLinkOpen(expectedUrl: String, openLink: () -> Unit) {
    Intents.init()
    try {
    openLink()
    intended(allOf(hasAction(ACTION_VIEW), hasData(expectedUrl)))
    } finally {
    Intents.release()
    }
    }

    View Slide

  14. Higher order functions
    verifyLinkOpen("https://buffer.com") {
    clickBufferButton()
    }

    View Slide

  15. Screen Robots

    View Slide

  16. Screen Robots
    class CalculatorScreenRobot : ScreenRobot() {
    fun verifyTipIsCorrect(tip: String): CalculatorScreenRobot {
    return checkViewHasText(R.id.textTip, tip)
    }
    fun inputCheckAmountAndSelectOkayButton(input: String):
    CalculatorScreenRobot {
    return enterTextIntoView(R.id.inputAmount, input)
    .clickOkOnView(R.id.buttonOkay)
    }
    }

    View Slide

  17. Screen Robots
    withRobot(CalculatorScreenRobot::class.java)
    .inputCheckAmountAndSelectOkayButton("11")
    .verifyTipIsCorrect("1.98")

    View Slide

  18. Screen Robots
    robot {
    inputCheckAmountAndSelectOkayButton("11")
    verifyTipIsCorrect("1.98")
    }

    View Slide

  19. Extension functions

    View Slide

  20. https://twitter.com/objcode/status/1123082385090908160

    View Slide

  21. Without Extension functions
    assert(contentViewModel.observeQueueState().value ==
    ContentState.Loading())

    View Slide

  22. Extension functions
    private fun ContentViewModel.hasState(state: ContentState) {
    assert(observeQueueState().value == state)
    }

    View Slide

  23. Extension functions
    contentViewModel.hasState(ContentState.Loading())

    View Slide

  24. Infix functions

    View Slide

  25. https://twitter.com/PreusslerBerlin/status/
    1123107023103696896
    https://twitter.com/jossiwolf/status/
    1123039773541707776

    View Slide

  26. Without Infix functions
    fun verifyTipIsCorrect(tip: String) {
    onView(withId(R.id.textTip))
    .check(matches(withText(tip)))
    }

    View Slide

  27. Infix functions
    infix fun Int.shouldHaveText(expected: String) {
    onView(withId(this))
    .check(matches(withText(expected)))
    }

    View Slide

  28. Infix functions
    fun verifyTipIsCorrect(tip: String) {
    R.id.textTip shouldHaveText tip
    }

    View Slide

  29. Combining multiple features
    inline fun > robot(block: T.() -> Unit) {
    T::class.java.newInstance().block()
    }
    robot {
    inputCheckAmountAndSelectOkayButton("11")
    verifyTipIsCorrect("1.98")
    }

    View Slide

  30. Data classes

    View Slide

  31. https://twitter.com/shekibobo/status/
    1123037412328988673
    https://twitter.com/tasomaniac/status/
    1123104225196683264

    View Slide

  32. Data classes
    data class UpdateData(
    var shareMode: ShareMode = ShareMode.ADD_TO_QUEUE,
    var media: MediaEntity? = null,
    var extraMedia: Array? = null,
    var retweet: RetweetEntity? = null,
    @Nullable var scheduledAt: Long? = null,
    var text: String = "",
    var facebookText: String = "",
    var profileIds: List? = null,
    var subProfileIds: List? = null,
    var sourceUrl: String? = null,
    var importId: String? = null,
    var shareDetails: ShareDetails? = null,
    var userChangedMedia: Boolean = false,
    var isEditing: Boolean = false,
    var editingProfile: ProfileEntity? = null,
    var networks: List? = null,
    var facebookTags: List? = null,
    var id: String? = "",
    var lastEditedDate: Long? = 0,
    var editingProfileTimezone: String? = null,
    val location: Location? = null,
    val commentEnabled: Boolean = false,
    val commentText: String? = null
    )

    View Slide

  33. Data classes
    UpdateData(null, null, emptyArray(), null, 0,
    "Hello World", "", emptyList(), emptyList(), null,
    "buffer.com", null, false, true,
    null, emptyList(), null, "", 0,
    null, null, false, "")

    View Slide

  34. Data classes
    UpdateData(text = "Hello World", sourceUrl = "buffer.com")

    View Slide

  35. Lazy property delegation

    View Slide

  36. https://twitter.com/POTUS404/status/1123210124108103680

    View Slide

  37. private val errorMessage by lazy { DataFactory.randomString() }

    View Slide

  38. Example
    https://youtu.be/wlb3lg5JocA

    View Slide

  39. Example
    https://youtu.be/wlb3lg5JocA

    View Slide

  40. Kotlin libraries

    View Slide

  41. Kakao

    View Slide

  42. https://twitter.com/erafn/status/1123067587125026816

    View Slide

  43. Kakao
    class SearchScreen : Screen() {
    val searchButton = KButton { withId(R.id.searchButton) }
    val snackbar = KView {
    withId(com.google.android.material.R.id.snackbar_text)
    }
    }

    View Slide

  44. Kakao
    screen {
    searchButton.click()
    snackbar.isDisplayed()
    }

    View Slide

  45. Spek

    View Slide

  46. Spek
    class ProfileSpec : Spek({
    given("twitter profile") {
    on("validate image count") {
    it("should be valid for four images") {
    }
    it("should fail for ten images") {
    }
    }
    }
    })

    View Slide

  47. Spek
    Spek:ProfileSpec:given twitter profile:on validate image count:it 

    should be valid for four images

    => java.lang.AssertionError: Expected , actual .

    View Slide

  48. Mockito-Kotlin

    View Slide

  49. https://twitter.com/Exallium/status/1123214566052323329

    View Slide

  50. Mockito
    val mockedUpdate = mock(Update::class.java)
    `when`(mockedUpdate.toString()).thenReturn("Update")

    View Slide

  51. Mockito-Kotlin
    val mockedUpdate = mock {
    on { toString() } doReturn "Update"
    }

    View Slide

  52. Kluent

    View Slide

  53. Kluent
    message shouldEqual "hello"
    message.shouldEqual("hello")
    message `should equal` "hello"

    View Slide

  54. Kluent
    invoking { read() } shouldThrow IOException::class

    View Slide

  55. Kluent
    Verify on mockedUpdate that mockedUpdate.toString() was called
    When calling stub.length itReturns 4

    View Slide

  56. Biscotti

    View Slide

  57. Biscotti
    verifyActivityLaunched(MainActivity::class.java.name) {
    onView(withId(R.id.button_faq))
    .perform(click())
    }

    View Slide

  58. https://vgonda.github.io/Talk-Resources/
    @TTGonda

    View Slide

  59. Thanks! Victoria Gonda
    victoriagonda.com
    @TTGonda

    View Slide