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

Make UI Testing Simple with a Hot Sip of Kakao

Make UI Testing Simple with a Hot Sip of Kakao

Android UI Testing uses Espresso library to match and validate the views inside the app. Even though it works really good, sometimes Espresso API can be very verbose and hard to understand. In this talk I introduced how you can make use of Kakao, an open source library that provides DSL for Espresso written in Kotlin, to simplify your UI Testing.

Malvin Sutanto

May 19, 2019
Tweet

More Decks by Malvin Sutanto

Other Decks in Technology

Transcript

  1. Make UI Testing Simple
    With a Hot Sip of Kakao

    View Slide

  2. Malvin Sutanto
    Software Engineer @Wantedly
    Visit Android & iOS
    Twitter/ Medium: @malvinsutanto

    View Slide

  3. Android UI Testing

    View Slide

  4. Consider This
    Simple
    Application

    View Slide

  5. Espresso
    1. @Test
    2. fun test_WhenFabIsPressed_ThenTextIsChanged() {
    3. val text = onView(withId(R.id.text))
    4. text.check(matches(withText("Hello World!")))
    5. onView(withId(R.id.text_input)).perform(ViewActions.typeText("My new string"))
    6. onView(withId(R.id.fab)).perform(ViewActions.click())
    7. text.check(matches(withText("My new string")))
    8. }

    View Slide

  6. Espresso
    ● Verbose
    ○ Lots of boilerplate codes
    ○ Cognitive burden
    ● Complex view layouts
    ○ Recycler views...
    ● Readability
    ○ View visibility, descendants, etc
    ● Difficult to maintain

    View Slide

  7. Espresso
    1. @Test
    2. fun test_WhenFabIsPressed_ThenTextIsChanged() {
    3. val text = onView(withId(R.id.text))
    4. text.check(matches(withText("Hello World!")))
    5. onView(withId(R.id.text_input)).perform(ViewActions.typeText("My new string"))
    6. onView(withId(R.id.fab)).perform(ViewActions.click())
    7. text.check(matches(withText("My new string")))
    8. }

    View Slide

  8. Espresso
    1. @Test
    2. fun test_WhenFabIsPressed_ThenTextIsChanged() {
    3. ))
    4. text.check(matches(withText("Hello World!")))
    5. onView(withId(R.id.text_input)).perform(ViewActions.typeText("My new string"))
    6. onView(withId(R.id.fab)).perform(ViewActions.click())
    7. text.check(matches(withText("My new string")))
    8. }

    View Slide

  9. Espresso
    1. @Test
    2. fun test_WhenFabIsPressed_ThenTextIsChanged() {
    3.
    4. text matchesText("Hello World!")))
    5. text_input typeText("My new string"))
    6. fab click())
    7. text.matchesText("My new string")))
    8. }

    View Slide

  10. Kakao

    View Slide

  11. Page Object Pattern

    View Slide

  12. Page Object Pattern
    ● Page:
    ○ A collection of logical UI elements.
    ● Has references to:
    ○ How to get hold of views.
    ○ Functions to manipulate that views.
    ● In Android
    ○ Can represent an Activity or a Fragment.
    ○ Or can be smaller...
    *https://martinfowler.com/bliki/PageObject.html

    View Slide

  13. Kakao
    https://github.com/agoda-com/Kakao
    ● DSL for Espresso in Kotlin
    ● Implements Page Object pattern
    ○ Screen
    ● Increase readability and reusability
    ● Extendability
    ● Nice and simple
    androidTestImplementation 'com.agoda.kakao:kakao:2.0.0'

    View Slide

  14. Screen
    1. class FormScreen : Screen() {
    2. val phone = KView { withId(R.id.phone) }
    3. val email = KEditText {
    4. withId(R.id.email)
    5. withText(R.string.email)
    6. }
    7. }

    View Slide

  15. KViews
    1. class FormScreen : Screen() {
    2. val phone = KView { withId(R.id.phone) }
    3. val email = KEditText {
    4. withId(R.id.email)
    5. withText(R.string.email)
    6. }
    7. }

    View Slide

  16. KViews
    1. class FormScreen : Screen() {
    2. val phone = KView { withId(R.id.phone) }
    3. val email = KEditText {
    4. withId(R.id.email)
    5. withText(R.string.email)
    6. }
    7. }

    View Slide

  17. Our Simple App

    View Slide

  18. Screen
    1. class AppScreen : Screen() {
    2. val text = KTextView { withId(R.id.text) }
    3. val textInput = KEditText { withId(R.id.text_input) }
    4. val fab = KButton { withId(R.id.fab) }
    5. }

    View Slide

  19. Screen
    1. class AppScreen : Screen() {
    2. val text = KTextView { withId(R.id.text) }
    3. val textInput = KEditText { withId(R.id.text_input) }
    4. val fab = KButton { withId(R.id.fab) }
    5. }

    View Slide

  20. Screen
    1. class AppScreen : Screen() {
    2. val text = KTextView { withId(R.id.text) }
    3. val textInput = KEditText { withId(R.id.text_input) }
    4. val fab = KButton { withId(R.id.fab) }
    5. }

    View Slide

  21. Screen
    1. class AppScreen : Screen() {
    2. val text = KTextView { withId(R.id.text) }
    3. val textInput = KEditText { withId(R.id.text_input) }
    4. val fab = KButton { withId(R.id.fab) }
    5. }

    View Slide

  22. Kakao
    1. @Test
    2. fun test_Kakao_WhenFabIsPressed_ThenTextIsChanged() {
    3. onScreen {
    4. text { hasText("Hello World!") }
    5. textInput { typeText("My new string") }
    6. fab { click() }
    7. text { hasText("My new string") }
    8. }
    9. }

    View Slide

  23. Kakao
    1. @Test
    2. fun test_Kakao_WhenFabIsPressed_ThenTextIsChanged() {
    3. onScreen {
    4. text { hasText("Hello World!") }
    5. textInput { typeText("My new string") }
    6. fab { click() }
    7. text { hasText("My new string") }
    8. }
    9. }

    View Slide

  24. Kakao
    1. @Test
    2. fun test_Kakao_WhenFabIsPressed_ThenTextIsChanged() {
    3. onScreen {
    4. text { hasText("Hello World!") }
    5. textInput { typeText("My new string") }
    6. fab { click() }
    7. text { hasText("My new string") }
    8. }
    9. }

    View Slide

  25. Kakao
    1. @Test
    2. fun test_Kakao_WhenFabIsPressed_ThenTextIsChanged() {
    3. onScreen {
    4. text { hasText("Hello World!") }
    5. textInput { typeText("My new string") }
    6. fab { click() }
    7. text { hasText("My new string") }
    8. }
    9. }

    View Slide

  26. Kakao
    1. @Test
    2. fun test_Kakao_WhenFabIsPressed_ThenTextIsChanged() {
    3. onScreen {
    4. text { hasText("Hello World!") }
    5. textInput { typeText("My new string") }
    6. fab { click() }
    7. text { hasText("My new string") }
    8. }
    9. }

    View Slide

  27. Let’s Compare...
    1. @Test
    2. fun test_WhenFabIsPressed_ThenTextIsChanged() {
    3. val text = onView(withId(R.id.text))
    4. text.check(matches(withText("Hello World!")))
    5. onView(withId(R.id.text_input)).perform(ViewActions.typeText("My new string"))
    6. onView(withId(R.id.fab)).perform(ViewActions.click())
    7. text.check(matches(withText("My new string")))
    8. }
    9.
    10. @Test
    11. fun test_Kakao_WhenFabIsPressed_ThenTextIsChanged() {
    12. onScreen {
    13. text { hasText("Hello World!") }
    14. textInput { typeText("My new string") }
    15. fab { click() }
    16. text { hasText("My new string") }
    17. }
    18. }

    View Slide

  28. Let’s Compare...
    1. @Test
    2. fun test_WhenFabIsPressed_ThenTextIsChanged() {
    3.
    4. text matchesText("Hello World!")))
    5. text_input typeText("My new string"))
    6. fab click())
    7. text.matchesText("My new string")))
    8. }
    9.
    10. @Test
    11. fun test_Kakao_WhenFabIsPressed_ThenTextIsChanged() {
    12. onScreen {
    13. text { hasText("Hello World!") }
    14. textInput { typeText("My new string") }
    15. fab { click() }
    16. text { hasText("My new string") }
    17. }
    18. }

    View Slide

  29. More Examples

    View Slide

  30. RecyclerView
    1. class Item(parent: Matcher) : KRecyclerItem(parent) {
    2. val title: KTextView = KTextView(parent) { withId(R.id.title) }
    3. val subtitle: KTextView = KTextView(parent) { withId(R.id.subtitle) }
    4. }
    1. val recycler: KRecyclerView = KRecyclerView({
    2. withId(R.id.recycler_view)
    3. }, itemTypeBuilder = {
    4. itemType(::Item)
    5. })
    1. onScreen {
    2. recycler {
    3. firstChild {
    4. isVisible()
    5. title { hasText("Title 1") }
    6. }
    7. }
    8. }

    View Slide

  31. Provide Your Own Matcher
    1. val textInput = KEditText {
    2. withId(R.id.text_input)
    3. withMatcher(hasImeAction(EditorInfo.IME_ACTION_SEND))
    4. }
    1. /**
    2. * Returns a matcher that matches views that support input methods (e.g. EditText) and have the
    3. * specified IME action set in its {@link EditorInfo}.
    4. *
    5. * @param imeAction the IME action to match
    6. */
    7. public static Matcher hasImeAction(int imeAction) {
    8. return hasImeAction(is(imeAction));
    9. }

    View Slide

  32. Summary

    View Slide

  33. Kakao
    ● Simple and easy to understand UI tests
    ● Make use of Espresso API
    ○ Reuse your existing matchers and assertions!
    ● Less code to write, more tests!

    View Slide

  34. Thank You

    View Slide