Back to Home

Kakao - how to make UI testing great again

translation · android · espresso · kotlin

Kakao - how to make UI testing great again

    image

    At Agoda, we pay a lot of attention to various types of testing our code. This helps us ensure its stability and find potential product problems at an early stage.

    At the moment, testing the user interface on Android for us is a small personal affiliate of hell. Google provides us with Espresso along with several extensions, a UI automator and so on. And although the tools do an excellent job of the task assigned to them, they do it not very beautifully. When you need to test a case that differs from a simple click on a button, your test code turns into a mess that is difficult to read. Just take a look:

    @Test
    public void espressoTest() {
      onView(allOf(allOf(withId(R.id.label_bf_hotelname), 
            isDescendantOfA(withId(R.id.custom_view_trip_review))), 
            isDescendantOfA(withId(R.id.contentView))))
            .check(matches(withEffectiveVisibility(View.VISIBLE)));
    }
    

    For me, this code seems unreadable. It is very difficult to support the work of more than 1000 UI tests of our Android application with similar syntax. But Google announced official support for the Kotlin language as a language for Android development at the Google I / O 2017 conference. Then we thought: “But we can try to migrate our tests to Kotlin!”

    But just translating the code from Java to Kotlin does not solve our test readability issue, as we are still dependent on Espresso. Fortunately, Kotlin provides us with many tools for creating DSL. With this in mind, we presented how our UI tests should look like on Kotlin. We wanted the test above to look like this:

    @Test
    fun espressoTest() {
      screen { hotelName { isVisible() } }
    }
    

    Looks great, don't you think so? Much more readable and easy to understand code than when using Espresso. And in most situations more compact. Did we manage to achieve this syntax? And how!

    image

    Meet Kakao - the simple Kotlin DSL for Android UI tests with Espresso. Kakao provides a convenient syntax for creating UI tests by abstracting your Activity / Fragment or View through the Screen class.

    open class TestActivityScreen: Screen() {
        val content: KView = KView { withId(R.id.content) }
        val map: KView = KView { withId(R.id.map) }
        val button: KButton = KButton { withId(R.id.button) }
        val textViewLarge: KTextView = KTextView { 
            withId(R.id.text_view_large) 
        }
        val textViewSmall: KTextView = KTextView { 
            withId(R.id.text_view_small) 
        }
    }
    

    To start using Kakao, you must enable Kotlin support in your project. You can find all the information you need for this on developer.android.com . Then you need to declare your hierarchy of interface elements using the classes that the library provides. All these classes (KView, KTextView, KButton) are empty classes that inherit the logic of the interfaces: Actions and Assertions. The library also provides helper classes for creating DSL-style matchers. One of these classes is called ViewBuilder and is used in the constructors of all KView descendants.

    class KTextView : KBaseView, TextViewAssertions {
        constructor(function: ViewBuilder.() -> Unit): super(function)
        constructor(parent: Matcher, function: ViewBuilder.() -> Unit): super(parent, function)
        constructor(parent: DataInteraction, function: ViewBuilder.() -> Unit): super(parent, function)
    }
    

    After declaring the hierarchy of the user interface, you can directly access the Screen object and all KView objects located in it using the invoke operator and perform various actions or statements in cascading style:

    @RunWith(AndroidJUnit4::class)
    class TestActivityTest {
      @Rule
      @JvmField
      val rule = ActivityTestRule(TestActivity::class.java)
      val screen = TestActivityScreen()
      @Test
      fun test() {
        screen {
          content { isVisible() }
          textViewLarge {
              click()
              isVisible()
              hasAnyText()
          }
          textViewSmall {
              isVisible()
              hasAnyText()
          }
          map {
              click()
              hasAnyTag("test_tag", "non_test_tag")
          }
          button { hasText("BUTTON") }
        }
      }
    }
    

    We have already translated most of our tests for using Kakao and are very pleased with the result. The library also supports more complex constructs such as RecyclerView, ListView, WebView and so on. It also has a convenient approach to customization, making it easy to create your own KView implementations with a different set of actions and statements, nested elements and logic.

    We decided to release this tool publicly so that other developers could start writing UI tests on Kotlin without having to stick to the Espresso syntax, which in our opinion is deprecated.

    You can connect Kakao to your project by simply adding this line to your build.gradle:

    androidTestCompile 'com.agoda.kakao:kakao:1.0.0'
    

    Join us on GitHub , rate Kakao and let's do great UI testing together again! Happy testing!

    Read Next