SkarpSkarp

Chapter 4 of 11

Locators, Accessibility, and Robust Screen Objects

Brittle selectors are the number one complaint in mobile UI testing. Build resilient locator strategies and screen objects that survive UI changes across Android and iOS.

15 min readen

Locators, Accessibility, and Robust Screen Objects – Why It Matters

Why Locators Matter

In mobile UI testing, brittle selectors are a top source of flaky tests. A tiny UI change can break many tests if your locator strategy is not robust.

What You Will Learn

You will learn to choose resilient locator strategies, use accessibility attributes for stable, readable tests, and implement Screen Objects that hide platform differences.

Your Starting Point

You already know how to run Appium tests with Java. Now we focus on what you point your tests at: the locators that identify elements on Android and iOS.

Modern Platforms and Accessibility

Modern Android (including Jetpack Compose) and iOS (UIKit, SwiftUI) treat accessibility attributes as first-class. You will use these to make tests robust and accessible.

Locator Types in Appium: The Toolkit

Locator Toolkit Overview

Appium supports locators like id, accessibility id, XPath, class name, and text-based strategies. Each has different trade-offs for speed and stability.

id Locators

`By.id()` maps to Android resource-id and often iOS name or accessibilityIdentifier. They are usually stable if defined in code, but can change with layout refactors.

Accessibility Id Locators

`MobileBy.AccessibilityId()` maps to Android contentDescription and iOS accessibilityIdentifier or label. They are human-readable and designed to be stable.

XPath and Class Name

XPath is flexible but often slow and brittle. Class name is simple but generic. Use them carefully, ideally combined with more stable attributes.

Text-Based Locators

Locating by visible text or label is easy but brittle. Text changes with copy edits, localization, or experiments, so avoid it for core tests.

Good vs Bad Locators on a Login Screen

Login Screen Scenario

Picture a login screen with username, password, a "Log in" button, and a "Forgot password?" link. You inspect the elements in Appium Inspector.

Android Attributes

Username has resource-id `com.shop.app:id/usernameinput` and content-desc `usernamefield`. Login button has resource-id `...:id/loginbutton` and content-desc `loginbutton`.

iOS Attributes

Username is `XCUIElementTypeTextField` with accessibilityIdentifier `usernamefield`. Login button is `XCUIElementTypeButton` with accessibilityIdentifier `loginbutton`.

Bad Locators

Bad: locate the login button by text `@text='Log in'` or username field by label `@label='Email or username'`. Text and labels change often.

Good Locators

Good: use `MobileBy.AccessibilityId("loginbutton")` and `MobileBy.AccessibilityId("usernamefield")`, or Android `By.id` where available. These are code-level identifiers.

Platform Differences: View Hierarchies and Attributes

Different Platforms, Different Trees

Android and iOS expose different attribute names and UI hierarchies even when the screen looks identical. This affects how your locators behave.

Android Attributes

Android gives resource-id, content-desc, text, and class (like android.widget.TextView). Jetpack Compose exposes semantics via contentDescription and test tags.

iOS Attributes

iOS exposes accessibilityIdentifier, label, value, and type (like XCUIElementTypeButton). It may flatten or merge elements for accessibility.

Impact on XPath

A working XPath on Android can fail on iOS because the tree structure differs. Deep, index-based XPath is especially brittle across platforms.

Strategy Summary

Prefer accessibility id for shared locators, fall back to platform-specific ids, and avoid deep or index-heavy XPath whenever possible.

Thought Exercise: Choosing the Best Locator

You are inspecting a "Product Details" screen with an "Add to cart" button. Here are the attributes you see.

Android Add to cart button

  • resource-id: `com.shop.app:id/primary_cta`
  • content-desc: `addtocart_button`
  • text: "Add to cart"
  • class: `android.widget.Button`

iOS Add to cart button

  • accessibilityIdentifier: `addtocart_button`
  • label: "Add to cart"
  • type: `XCUIElementTypeButton`

Task 1

List two locators you could use in a cross-platform test that should remain stable even if the visible text changes to "Buy now".

Write your answer in your own words before checking the explanation below.

Suggested answer (do not peek until you try):

  • Use `MobileBy.AccessibilityId("addtocart_button")` in a shared Screen Object method.
  • If you must support an older Android build without content-desc set, use platform-specific ids in the Screen Object: `By.id("com.shop.app:id/primarycta")` for Android and `MobileBy.AccessibilityId("addtocartbutton")` for iOS, while exposing a single `tapAddToCart()` method to tests.

Reflection

Notice how the identifier stays stable even if marketing changes the button text. That is the core idea behind robust locators.

Accessibility Attributes as Testing Superpowers

Accessibility for Testing

Accessibility attributes help screen readers and also give you stable, semantic locators that survive layout and text changes.

Android Accessibility

Android uses contentDescription, shown as content-desc in Appium. Jetpack Compose can map test tags to these semantics for testing.

iOS Accessibility

iOS prefers accessibilityIdentifier for testing. accessibilityLabel often matches visible text and can change more frequently.

Best Practices

Ask devs to set explicit accessibility identifiers, use MobileBy.AccessibilityId in tests, and avoid labels for core test flows.

Benefits

You gain stable tests, better accessibility, and a clear shared language with developers: refer to elements by their accessibility id.

Implementing a Cross-Platform Screen Object in Java

Now you will see how to hide Android/iOS differences behind a single Screen Object using Java and Appium.

Key ideas:

  • Use composition or inheritance to share behavior.
  • Keep locators private; expose only meaningful actions.
  • Use `MobileBy.AccessibilityId` wherever possible.

Below is a simple `LoginScreen` example that works for both Android and iOS. It assumes that both platforms expose `usernamefield`, `passwordfield`, and `login_button` as accessibility identifiers.

Focus on:

  • How locators are defined once.
  • How tests call clear, high-level methods like `loginAs` instead of dealing with locators directly.

Handling Platform-Specific Locators Behind One API

Sometimes Android and iOS cannot share the exact same locator. For example, Android has a stable `resource-id` while iOS only exposes an `accessibilityIdentifier`.

You can still present a single, clean API to your tests by branching inside the Screen Object. Here is an example `ProductDetailsScreen` with platform-specific locators for the price label and shared accessibility id for the "Add to cart" button.

Notice:

  • Tests call `getPriceText()` and `tapAddToCart()` without caring about the platform.
  • The Screen Object inspects the driver platform and chooses the right locators internally.

Check Your Locator Strategy

Answer this quick question to check your understanding of robust locators.

Which locator is usually the MOST robust choice for a cross-platform, high-value button that has a stable accessibility identifier set by developers?

  1. By.xpath("//android.widget.Button[@text='Pay now']")
  2. MobileBy.AccessibilityId("primary_payment_button")
  3. By.className("XCUIElementTypeButton")
Show Answer

Answer: B) MobileBy.AccessibilityId("primary_payment_button")

MobileBy.AccessibilityId("primary_payment_button") targets the shared accessibility identifier, which is designed to be stable across UI changes and platforms. XPath by visible text is brittle, and className alone is too generic.

Review: Key Terms and Ideas

Use these flashcards to quickly review the main concepts from this module.

Accessibility id (Appium)
A locator strategy (`MobileBy.AccessibilityId`) that maps to Android content-desc and iOS accessibilityIdentifier/label. Preferred for stable, human-readable locators.
Brittle locator
A locator that breaks easily when the UI changes, for example XPath based on deep hierarchies or visible text that often changes.
Screen Object (Page Object) Model
A design pattern where each app screen is represented by a class that encapsulates locators and exposes high-level actions like `loginAs`.
resource-id vs accessibilityIdentifier
Android resource-id is a view id defined in layouts; iOS accessibilityIdentifier is a string set for accessibility and testing. Both can be used as stable ids.
Platform-specific locator handling
A technique where a Screen Object chooses different locators for Android and iOS internally, while exposing a single, shared API to tests.

Key Terms

XPath
A query language used to navigate UI hierarchies; powerful but often slower and more brittle than id-based locators.
resource-id
Android view identifier defined in XML layouts or code, typically mapped to `By.id` in Appium.
Brittle locator
A selector that frequently breaks due to minor UI or text changes, causing flaky tests.
Accessibility id
Appium locator strategy (`MobileBy.AccessibilityId`) that maps to Android content-desc and iOS accessibilityIdentifier/label, used for stable, semantic element identification.
Screen Object Model
A pattern similar to Page Object Model where each mobile app screen is represented by a class encapsulating locators and user actions.
accessibilityIdentifier
An iOS property used to uniquely identify UI elements for testing and accessibility; maps well to Appium accessibility id.
contentDescription (content-desc)
Android attribute that provides an accessibility description of a view; used by screen readers and Appium accessibility id.

Finished reading?

Test your understanding with a custom practice exam on this chapter.

Test yourself