skip to content

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