Android E2E Automaton Test with UI Automator 🤖

Photo by ZMorph Multitool 3D Printer on Unsplash

Like what you see? This is achieved with the help of UI Automator. A powerful testing framework that provides APIs to simulate application wide and platform wide operations. The most used two in my opinion are:

  1. BySelector/UiSelector: Represents a query for one or more target UI elements on a device.
  2. UiObject: Represents a UI element that is visible on the device.

Suppose in our app, we have a page with a list of blog posts which we can navigate to by clicking on the hamburger menu then clicking on the Posts text. On the page we have a carousel widget that auto plays our posts by default. We can click on the cover image and navigate to a post detail page. And when clicking back button, we should be brought back to the blog posts page. Now we want to set up a test for this set of interactions. This is a simple yet perfect use case for UI Automator.

Add dependency

dependencies {
androidTestImplementation'androidx.test.uiautomator:uiautomator:2.2.0'
}

Set up UI Automator

private lateinit var device: UiDevice

@Before
fun init() {
// Initialize UiDevice instance
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

// Find and open hamburger menu
device.findObject(By.desc("Open navigation drawer")).click()

// Find and navigate to Posts page
onView(allOf(withResourceName("menu_item"),withText("Posts"))).perform(click())
}

Test

/**
* Carousel component should navigate to and back from post detail page
*/
@Test
fun carouselNavigatesToAndBackFromDetailPage() {
// Make sure carousel navigates to post detail page upon click
onView(withId(R.id.carouselPostCoverImage)).perform(click())
// Blog posts are not visible
onView(withId(R.id.postsFragmentLayout)).check(doesNotExist())
// Detail fragment layout should be visible
onView(withId(R.id.detailFragmentLayout)).check(matches(isDisplayed()))

pressBack()

// Blog posts are displayed
onView(withId(R.id.postsFragmentLayout)).check(matches(isDisplayed()))
// Detail fragment is not visible
onView(withId(R.id.detailFragmentLayout)).check(doesNotExist())
}

Complete code

/**
* E2E test for BlogPosts page
*/
@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = 18)
class BlogPostsTest {
/**
* Clear app local storage
*/
@get:Rule
val clearLocalStorageRule = ClearLocalStorageRule(MainActivity::class.java)

/**
* Handle async actions
*/
@get:Rule
val idlingResourceRule = IdlingResourceRule()

private lateinit var device: UiDevice

@Before
fun init() {
// Initialize UiDevice instance
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

// Find and open hamburger menu
device.findObject(By.desc("Open navigation drawer")).click()

// Find and navigate to Posts page
onView(allOf(withResourceName("menu_item"),withText("Posts"))).perform(click())
}

/**
* Carousel component should navigate to and back from post detail page
*/
@Test
fun carouselNavigatesToAndBackFromDetailPage() {
// Make sure carousel navigates to post detail page upon click
onView(withId(R.id.carouselPostCoverImage)).perform(click())
// Blog posts are not visible
onView(withId(R.id.postsFragmentLayout)).check(doesNotExist())
// Detail fragment layout should be visible
onView(withId(R.id.detailFragmentLayout)).check(matches(isDisplayed()))

pressBack()

// Blog posts are displayed
onView(withId(R.id.postsFragmentLayout)).check(matches(isDisplayed()))
// Detail fragment is not visible
onView(withId(R.id.detailFragmentLayout)).check(doesNotExist())
}
}

As you can see I have used two custom test rules which you can ignore for now. I will talk more about custom test rules in a future post. As a general rule of thumb, UI/automation tests can grow relatively large in size which increases fidelity but also increases execution time and effort to maintain and debug. Therefore, we should keep the amount of these tests small.

Example of test pyramid

Hope you have an idea about UI Automator and can start writing a simple E2E test now. If you have enjoyed this article please give it a clap, I will see you in the future posts.

Keep calm and code on!

--

--

--

Software Developer | Tech Junkie | Professional Nap Taker 🐨

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Get ahead using headers in RecyclerView

2 illustrated phones. One without header and one with header.

Jetpack Compose Ep:1- Just Text App

How to sign your React Native app for android

TASK 1 :

New Feature — Camera Shake

Using Google Glass in 2020

How to implement a dark theme on Android

Android | Data Binding And Two-way Data Binding

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
Brian Wang

Brian Wang

Software Developer | Tech Junkie | Professional Nap Taker 🐨

More from Medium

Android login screen using jetpack compose [Part-2]

How to write and to read files from Kotlin in JetPack Compose

Koin Android

Dialog in Android Jetpack Compose with MVVM