Espresso Robot Pattern in Kotlin

Faruk Toptaş
Android Bits
Published in
3 min readDec 30, 2017

--

Espresso allows us to write Android UI tests. Creating automated tests should be a part of our development process. But it is not always easy to create a full set UI tests.

Robot pattern fits with Espresso and allows to create clear and understandable tests. You can see more details from its creator Jake Wharton.

The main aim is separating the WHAT from the HOW.

Basically a robot is a class with some methods related to actions on a view. It looks like Builder pattern.

Let’s see how we do it before robots:

onView(withId(resId))
.perform(ViewActions.replaceText(text),ViewActions.closeSoftKeyboard())

The code above is for filling an EditText with a string.

For a simple Login screen I need to code more for a test case.

Espresso.onView(ViewMatchers.withId(R.id.etEmail))
.perform(ViewActions.replaceText("mail@example.com"), ViewActions.closeSoftKeyboard())

Espresso.onView(ViewMatchers.withId(R.id.etPassword))
.perform(ViewActions.replaceText("wrong_password"), ViewActions.closeSoftKeyboard())

Espresso.onView((ViewMatchers.withId(R.id.btnLogin))).perform(ViewActions.click())

Espresso.onView(ViewMatchers.withId(android.R.id.message))
.check(ViewAssertions.matches(ViewMatchers.withText(mActivityTestRule.activity.getString(R.string.login_fail))))

Before creating a LoginRobot I will create a BaseTestRobot for simple actions:

open class BaseTestRobot {

fun fillEditText(resId: Int, text: String): ViewInteraction =
onView(withId(resId)).perform(ViewActions.replaceText(text), ViewActions.closeSoftKeyboard())

fun clickButton(resId: Int): ViewInteraction = onView((withId(resId))).perform(ViewActions.click())

fun textView(resId: Int): ViewInteraction = onView(withId(resId))

fun matchText(viewInteraction: ViewInteraction, text: String): ViewInteraction = viewInteraction
.check(ViewAssertions.matches(ViewMatchers.withText(text)))

fun matchText(resId: Int, text: String): ViewInteraction = matchText(textView(resId), text)

fun clickListItem(listRes: Int, position: Int) {
onData(anything())
.inAdapterView(allOf(withId(listRes)))
.atPosition(position).perform(ViewActions.click())
}


}

Like Builder pattern each method should return itself. So all actions can be chained step by step.

class LoginRobot : BaseTestRobot() {

fun setEmail(email: String) = fillEditText(R.id.etEmail, email);

fun setPassword(pass: String) = fillEditText(R.id.etPassword, pass)

fun clickLogin() = clickButton(R.id.btnLogin)

fun matchErrorText(err: String) = matchText(textView(android.R.id.message), err)

}

By using the power of Kotlin we make each fun return the object itself with apply.

fun login(func: LoginRobot.() -> Unit) = LoginRobot()
.apply { func() }

Here is the usage of the robot. Simple, clean and understandable.

@Test
fun loginMissingPassword() {
login {
setEmail
("mail@example.com")
clickLogin()
matchErrorText(string(R.string.missing_fields))
}
}
500ms delays added between steps to create the GIF

Each screen has its own robot. We can use multiple robots in a test scenario.

@Test
fun loginProfileAndSettings() {
login {
setEmail
("mail@example.com")
setPassword("pass")
clickLogin()
}
profile
{
clickSettings
()
toggleNotifications()
toggleNightMode()
}
}

With robots it is possible to create a layered UI Test architecture like app architecture.

After setting each unique steps for a screen. All cases can be created easily.

@Test
fun loginMissingEmailPassword() {
login {
clickLogin()
matchErrorText(string(R.string.missing_fields))
}
}
@Test
fun loginMissingPassword() {
login {
setEmail("mail@example.com")
clickLogin()
matchErrorText(string(R.string.missing_fields))
}
}
@Test
fun loginWrongPassword() {
login {
setEmail("mail@example.com")
setPassword("wrong")
clickLogin()
matchErrorText(string(R.string.login_fail))
}
}@Test
fun loginSuccess() {
login {
setEmail("mail@example.com")
setPassword("pass")
clickLogin()
matchText(R.id.tvName, string(R.string.name_surname))
}
}
@Test
fun loginProfileAndSettings() {
login {
setEmail("mail@example.com")
setPassword("pass")
clickLogin()
}
profile {
clickSettings()
toggleNotifications()
toggleNightMode()
}
}

Now you can make fun of lovely green steps :)

You can see the full source code of this sample on Github.

What’s next?

Resources:

Happy codings :)

Clap as much as you can if you like this article.

--

--