developing test scripts in kotlin

espresso test robots

Posted on January 15, 2020

I was working with Java so it wasn’t a problem to switch to Kotlin. Indeed, Kotlin is cool, its features help a lot to reduce code lines, it provides null-safety mechanism, data classes and other cool features.

My plan is to highlight some language features and give a short example of usage. Of course, it is not a complete overview of all existing features, just those of them that I used when developing test scripts with Espresso for Android native app.

So let’s start.

robot pattern + kotlin (objects, apply())

You may heard about Page Object pattern that is widely used in test automation. For Espresso tests there is Robot pattern - each ‘robot’ contains functions that correspond to specific user actions on a specific (one) screen. Robots can be implemented in couple of ways especially with the power of Kotlin.

Robot class. We are using object keyword to create a ‘robot’ class, it means that this class will be a singleton:

//imports

object EnvironmentsRobot {

    fun chooseStage = apply {
        onView(withId(R.id.stageEnv)).perform(click())
    }

    fun chooseTest= apply {
        onView(withId(R.id.testEnv)).perform(click())
    }

    fun chooseProd = apply {
        onView(withId(R.id.prodEnv)).perform(click())
    }
    
    fun close = apply {
        onView(withId(R.id.close)).perform(click())
    }
    
}

And in your test you can use it as follows:

    @Test
    fun switchEnvironmentTest() {
        EnvironmentsRobot
            .chooseTest()
            .close()
            .chooseStage()
            .close()
            .chooseProd()
            .close()
    }          

We make use of Kotlin objects and scope function apply() to enable method chaining. You can read more about scope functions here.

But we can go further to make test code more readable and compact. Let’s modify our ‘robot’. Again we are using apply() function and it returns the object itself. Also note that our ‘robot’ is not an object anymore. It is a simple class now:

//imports

fun switchEnvironmentRobot(func: EnvironmentsRobot.() -> Unit) = EnvironmentsRobot()
        .apply { func() }

class EnvironmentsRobot {

    fun chooseStage() = onView(withId(R.id.stageEnv)).perform(click())

    fun chooseTest() = onView(withId(R.id.testEnv)).perform(click())

    fun chooseProd() = onView(withId(R.id.prodEnv)).perform(click())

    fun close() = onView(withId(R.id.close)).perform(click())

}

And here’s an updated test:

    @Test
    fun switchEnvironmentsTest() {
        switchEnvironmentRobot {
            chooseStage()
            close()
            chooseTest()
            close()
            chooseProd()
            close()
        }
    }

You can create as many robots as needed and use them in one test. The test will look like this:

    @Test
    fun multipleRobotsTest() {
    
        switchEnvironmentRobot {
            //actions
        }
        
        loginRobot {
            //actions
        }    
        
        syncRobot {
            //actions
        }      
        
    }

Simple and clear.