skip to content

Locator Longevity

Why an address that worked last release does not work this one: the app side has to publish a stable field, and the tree the driver reads is a bounded snapshot taken per call.

on this pageshow

explore

questions

14

In a React Native app under Appium, what does the `testID` prop set on iOS and on Android?

level: juniorimportance: must knowfreq 63%

answer

  1. one prop, two different app-side fields
  2. iOS gets the identifier, not the label
  3. Android reports it as resource-id
  4. content-desc comes from accessibilityLabel instead

basics

~20 s

A React Native testID sets the iOS accessibilityIdentifier, which the XCUITest driver exposes behind the name attribute, and on Android it surfaces as the view's resource-id in the page source. Android's content-desc is filled by accessibilityLabel instead.

solid answer

~40 s

`testID` is React Native's one cross-platform hook for automation, but it lands in two different app-side fields. On **iOS** it sets the view's `accessibilityIdentifier`; the XCUITest driver surfaces that behind the `name` attribute and aliases both the `id` and the `accessibility id` strategies onto `name`, so either one finds the element. On **Android** the value surfaces as the view's `resource-id`, which is what Appium's `id` strategy matches — it does *not* land in `content-desc`, and `content-desc` is exactly what `accessibility id` matches there. Android's `content-desc` comes from React Native's `accessibilityLabel`. That asymmetry is why an id a suite finds on one platform can return nothing on the other, and why teams end up asking the app to fill more than one field.

go deeper

for a junior

Be ready to say that testID sets the iOS accessibilityIdentifier and surfaces as resource-id on Android. Naming the two fields, not just the prop, is what the question is testing.

for a middle

Explain why an accessibility id locator misses a testID on Android: that strategy matches content-desc, which accessibilityLabel fills, while testID lands in resource-id.

for a senior

Show how you verify that an id landed on each platform before a suite leans on it, and what you do about screens where the prop never reached the native view.

for a principal

Decide which app-side field each platform will fill across the whole app, so cross-platform screens and hand-written native screens present the same identifier to a suite.

## One prop, two app-side fields React Native gives a component a `testID` prop so that automation can address it later. Appium never sees that prop. Appium talks to a driver, the driver reads the platform's own view tree, and a locator matches an **attribute that a native view publishes**. `testID` matters only because the React Native renderer writes it into a native field before Appium ever looks — and it writes into a *different* field on each platform. That is the whole content of this topic: one prop, two destinations, and a suite that has to know which one it is addressing. ## Where the value lands on each platform On **iOS**, `testID` sets the view's `accessibilityIdentifier`. The XCUITest driver surfaces that value behind the `name` attribute, and it aliases both the `id` and the `accessibility id` locator strategies onto `name`. Either strategy therefore finds the element, and on that platform three ways of asking collapse into one. On **Android**, the value surfaces as the view's `resource-id`, which is the attribute Appium's `id` strategy matches. It does **not** land in `content-desc` — and `content-desc` is exactly what the `accessibility id` strategy matches on Android. That field is filled by React Native's `accessibilityLabel`. So on Android the two strategies address two genuinely different fields, written by two different props. | | Android | iOS | |---|---|---| | Field `testID` fills | the view id, reported as `resource-id` | `accessibilityIdentifier` | | Attribute it appears under | `resource-id` | `name` | | Strategy that matches it | `id` | `id` and `accessibility id` | | Field `accessibility id` matches | `content-desc` | `name` | | Prop that fills that field | `accessibilityLabel` | `accessibilityLabel`, only when the identifier is empty | ## Why the asymmetry bites - A scouting badge-tracker sets a `testID` of `badge-row-first-aid` on a badge row, the iOS suite finds it with `accessibility id`, and the identical locator returns nothing on Android. - Reaching for Android's `id` strategy is the right correction, but the string the renderer wrote carries no package prefix, so a bare `id` locator can still miss until the driver's id autocompletion is turned off. - Adding `accessibilityLabel` does make `accessibility id` work on Android, at the price of coupling the locator to user-facing text that a content team may reword. - On iOS the reverse trap waits: when `accessibilityIdentifier` is empty, `name` resolves to the element's label instead, so the find still succeeds — against localised copy — and the missing identifier stays invisible until the app ships in a second language. - A `testID` set on a wrapper component that does not forward props down to its underlying native view reaches no field at all, and the page source simply shows an empty attribute. ## Confirming the id actually landed The prop is a request, not a guarantee, so verify per platform before the suite depends on the value: 1. Run the screen on Android and look for the string in the `resource-id` attribute of the page source. 2. Run the same screen on iOS and look for the same string under `name`. 3. Only then write the locator; a screen that fails either check is an app change that is not finished. ## What teams usually settle on - Treat `testID` as the machine identity and the thing the suite addresses. - Set `accessibilityLabel` separately for human-facing text, and never assume the two are interchangeable. - Expect the Android locator and the iOS locator to be written against different strategies, unless the app deliberately fills Android's `content-desc` as well. - Keep the value stable across releases: it exists for the suite, so a rename is a breaking change even though nothing visible moved. - Address elements one screen at a time, because the plumbing is a per-screen property of the app rather than a global switch. The short version worth being able to say out loud: `testID` is React Native's cross-platform hook, but the platforms are not symmetric underneath it. iOS gets a dedicated machine field that the driver reads behind `name`; Android gets the view id reported as `resource-id`, while the field an `accessibility id` locator reads on Android is filled by a different prop entirely. Anyone who says that `testID` sets the accessibility id on both platforms has skipped the only thing this topic is about.

  • If the scouting badge-tracker sets `testID` but no `accessibilityLabel`, which Android locator strategies can still match it?
    `id` can, because the value surfaces as the view's `resource-id`. `accessibility id` cannot: on Android that strategy matches `content-desc`, which stays empty without `accessibilityLabel`. XPath over the same `resource-id` attribute works too, at page-source cost. On iOS the single `testID` covers both strategies, because XCUITest aliases `id` and `accessibility id` onto `name`.
  • Does setting `testID` on a React Native component guarantee that Appium sees it on both platforms?
    No. It has to reach a real native view. A `testID` on a component that does not forward props to its underlying native view never becomes an `accessibilityIdentifier` on iOS or a view id on Android, and the page source shows an empty attribute. Confirm the value per platform in the page source before a suite leans on it.

A test id is like a part number stamped inside a machine: the same number can be stamped in a different place on each model. Knowing the number is useless until you know where each model stamps it.

saying these in an interview costs you the question

  • Thinks testID sets content-desc on Android
  • Assumes one prop produces one identical field on both platforms
  • Believes an accessibility id locator matches testID on Android
  • Treats a visible label and a machine test id as interchangeable
  • Assumes the prop always reaches the underlying native view
open as a page

In Appium, which attribute tells you a switch is on for Android, and which for iOS?

level: middleimportance: must knowfreq 62%

basics

~20 s

Android's UiAutomator2 driver exposes the switch position as its own attribute, checked, reading back as true or false. iOS's XCUITest driver folds the position into the general value attribute, where an on switch reads back as 1.

open as a page

Which Appium settings bound the page-source snapshot on Android, and which do so on iOS?

level: middleimportance: must knowfreq 46%

basics

~10 s

Both drivers honour snapshotMaxDepth. Android's UiAutomator2 adds allowInvisibleElements, ignoreUnimportantViews and enableMultiWindows; Apple's XCUITest adds snapshotMaxChildren, useJSONSource, includeHittableInPageSource and pageSourceExcludedAttributes. Same job, almost entirely different names.

open as a page

In Appium, what is the page-source tree actually built from on Android compared with iOS?

level: middleimportance: must knowfreq 58%

basics

~20 s

Android's UiAutomator2 driver serialises the accessibility node hierarchy the platform publishes. Apple's XCUITest driver serialises an XCUIElement snapshot WebDriverAgent takes through XCTest. Neither is the app's own view hierarchy, so whatever those layers withhold is unaddressable.

open as a page

In Appium on Android, what does the UiAutomator2 setting `disableIdLocatorAutocompletion` change about an `id` locator?

level: middleimportance: must knowfreq 51%

basics

~20 s

By default Appium's UiAutomator2 driver completes a bare id locator with the app package before matching Android's resource-id. Setting disableIdLocatorAutocompletion to true makes the driver match the string exactly as written, which is what a generated test id needs.

open as a page

In Appium, which request reads one attribute from an element on Android and iOS?

level: juniorimportance: should knowfreq 71%

basics

~10 s

Both platforms use the same request: GET /session/:sessionId/element/:elementId/attribute/:name, with an element identifier from an earlier find and exactly one attribute name in the final segment. Only which names are valid differs by platform.

open as a page

In Appium, which request returns the current page source on Android, and what does iOS add?

level: juniorimportance: should knowfreq 64%

basics

~10 s

Both platforms answer the W3C endpoint GET /session/:sessionId/source, which returns the current screen as one XML tree. Apple's XCUITest driver additionally exposes a mobile: source execute method; the Android drivers ship no equivalent.

open as a page

In Appium, how do Android's displayed attribute and iOS's visible and hittable differ?

level: middleimportance: should knowfreq 54%

basics

~20 s

Android's UiAutomator2 driver answers the whole question with one attribute, displayed. iOS's XCUITest driver splits it in two: visible says the element is drawn, hittable says a touch aimed at it would actually land on it.

open as a page

In Appium on Android, why is a Jetpack Compose test tag unmatchable until you set `mapTestTagToResourceId`?

level: middleimportance: should knowfreq 38%

basics

~20 s

A Compose test tag lives in Compose's own semantics description, and Appium's UiAutomator2 driver reads Android's accessibility node tree instead. The UiAutomator2 setting mapTestTagToResourceId is what reports that tag in the resource-id slot so an id locator can reach it.

open as a page

How would you write one Appium switch-state assertion that works on Android and iOS?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Map the fact, not the attribute: hold one pair per platform - checked with true on Android's UiAutomator2 driver, value with 1 on iOS's XCUITest driver - normalise the reply to a boolean at the read, and assert on that.

open as a page

In an Appium translation-glossary suite, an iOS locator finds nothing though the term is on screen — how do you triage?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Capture GET /session/:sessionId/source at the moment of failure and search it for the term. If the node is absent, no XCUITest locator can match it: widen snapshotMaxDepth or snapshotMaxChildren, or fix the app. If present, the locator is wrong.

open as a page

In Appium, why does a test id kept in Android's content-desc break on a copy change when an iOS accessibilityIdentifier does not?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Android publishes one description field, so content-desc is both what an accessibility id locator matches and what the platform announces, and rewriting the copy rewrites the locator. iOS splits the two: accessibilityIdentifier holds the machine id and the label holds the text.

open as a page

How would you get one test id into the Android and iOS fields Appium matches across a mixed-stack scouting badge-tracker?

level: principalimportance: should knowfreq 36%

basics

~20 s

Pick one field pair for the whole app, then make every stack fill it: Android resource-id or content-desc, and the iOS accessibilityIdentifier. Put the Android driver settings in the session profile, and verify per screen that the value reached the page source.

open as a page

In Appium, what do shouldUseCompactResponses and elementResponseAttributes change?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

Both settings control how much of an element comes back with a find. In compact mode the reply carries only the element identifier, so every attribute costs its own request; turning it off makes the driver inline the attributes named in elementResponseAttributes.

open as a page