Automating React Native on iOS When the Page Source Is Unreadable
Short answer: the nested wrappers you see in Appium Inspector are a tooling problem, not a
React Native problem. The testID you set in JSX does reach the native layer on both platforms —
it just gets buried in a hierarchy most inspectors render unusably deep. Fix what you inspect
with, add testID consistently, and one locator will drive both iOS and Android.
Why the iOS tree looks like that
React Native composes its UI from host views. A single <Pressable> with a <Text> inside can
land as several nested XCUIElementTypeOther nodes, none of which is obviously "the button".
Dump that tree and you get forty levels of scaffolding, so people fall back to XPath expressions
that describe position rather than identity:
// Brittle: describes where the element sits today
driver.findElement(AppiumBy.xpath(
"//XCUIElementTypeOther[3]/XCUIElementTypeOther[2]/XCUIElementTypeButton[1]"));That locator survives exactly until someone wraps the view in one more <View> for layout.
Android usually hurts less, because the view hierarchy is flatter and resource-id gives you
something stable to grab. The asymmetry is why teams end up with two locator strategies for one
codebase — and twice the maintenance.
The fix starts in the app, not the test
Set testID on every element a test needs to touch. React Native maps it to the
accessibility identifier on iOS and the content description / resource-id path on Android,
so the same string works on both:
<Pressable testID="checkout-button" onPress={checkout}>
<Text>Check out</Text>
</Pressable>// One locator, both platforms
driver.findElement(AppiumBy.accessibilityId("checkout-button")).click();Two rules make this hold up over time:
- Name the role, not the design.
checkout-button, nevergreen-cta-v2. A design-derived name is guaranteed to be wrong after the next redesign. - Put it on the pressable, not the label.
testIDon a<Text>inside a<Pressable>finds the text node, and taps on it may not register as taps on the button.
If you inherited an app with no testIDs, add them to the twenty elements your smoke suite
touches before you do anything else. That is usually a single afternoon and it removes most of
the XPath.
But the ids still have to be visible
Here is where teams get stuck. You add testID correctly, and the iOS inspector still shows a
wall of XCUIElementTypeOther. The identifier is there; the tool is not surfacing it in a form
you can act on. So people conclude React Native on iOS is simply hard to automate, and go back
to XPath.
It is worth checking that assumption against a different inspector before rewriting your test
strategy around it. In the RobotActions inspector a React Native app resolves the way any native
app does — named, addressable elements rather than wrapper soup — which is the difference between
accessibilityId("checkout-button") being available to you and not.
That is the whole claim, and it takes about ten minutes on the free tier to confirm or dismiss on your own build.
Verify on real hardware
Simulators hide the two things most likely to break a React Native screen:
- Gesture timing. Synthesised swipes send two coordinates and skip everything in between. Drag handles, carousels, sliders and pull-to-refresh only behave correctly under a continuous stream of pointer events.
- Permission and system dialogs. OEM Android skins and iOS permission prompts appear at different moments on real devices than in a simulator, and a test that never sees them passes for the wrong reason.
Both are cheap to check once the app is installed on a real phone you can drive from a browser.
A reasonable order of work
- Add
testIDto the elements your smoke suite touches. Role-based names. - Inspect on a real iOS device and confirm the ids are actually addressable.
- Replace XPath with
accessibilityIdwherever step 2 made it possible. - Run the same suite on Android — most of those locators should now work unchanged.
- Keep XPath only for the handful of cases nothing else reaches, and comment why.
The goal is not zero XPath. It is that a redesign changes the layout without changing which elements your tests can find.
Related
Ready to test on real devices?
Sign in with Google or GitHub and get real iOS and Android devices in your browser — free to try.