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.
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?
- By.xpath("//android.widget.Button[@text='Pay now']")
- MobileBy.AccessibilityId("primary_payment_button")
- 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.