Android E2E Automaton Test with UI Automator 🤖
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:
- BySelector/UiSelector: Represents a query for one or more target UI elements on a device.
- 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.
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!