Espresso Robot Pattern in Kotlin

Faruk Toptaş
Dec 30, 2017 · 3 min read

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.

Image for post
Image for post

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))
}
}
Image for post
Image for post
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 :)

Image for post
Image for post

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.

Android Bits

Awesome Blog From Android Folks

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store