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 full-size slide

  2. Testing with Kotlin

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  5. Kotlin features

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  10. Higher order functions

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  14. Screen Robots

    View full-size slide

  15. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  18. Extension functions

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  23. Infix functions

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  29. Data classes

    View full-size slide

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

    View full-size slide

  31. 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 full-size slide

  32. 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 full-size slide

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

    View full-size slide

  34. Lazy property delegation

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  39. Kotlin libraries

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  43. 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 full-size slide

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

    should be valid for four images

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

    View full-size slide

  45. Mockito-Kotlin

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  54. Thanks! Victoria Gonda
    victoriagonda.com
    @TTGonda

    View full-size slide