Skip to main content
Version: Next

Adding testID to your components

Note

This guide is applicable only for React Native applications.

It is always the best idea to match your element by something unique. We recommend using testID property supported by most React Native components:

<View>
<TouchableOpacity testID="MyUniqueId123">
<Text>Some text</Text>
</TouchableOpacity>
</View>

Pass testID to your native components

Passing a testID to your custom component props has no effect until you forward it down to a native component like <View /> or <TouchableOpacity /> that implements rendering it as an accessibility identifier in the native component hierarchy:

Pass testID to native component

For example, you have <YourCustomComponent /> and you pass a testID to it:

YourScreen.jsx
function YourScreen() {
return (
<YourCustomComponent testID="YourCustomComponent" />
);
}

Make sure that your implementation passes testID to some React Native component that supports it:

YourCustomComponent.jsx
function YourCustomComponent(props) {
return (
<View testID={props.testID}>
<Text>Some text</Text>
</View>
);
}

Child elements

If your component has several useful child elements, it is even a better idea to assign them some derived test IDs, e.g.:

YourCustomComponent.jsx
function YourCustomComponent(props) {
return (
<View testID={props.testID}>
<Text testID={`${props.testID}.label`}>Some text</Text>
</View>
);
}

That way, you could refer to specific elements in Detox tests via the most basic and least ambiguous by.id matchers, e.g.:

expect(element(by.id('YourCustomComponent'))).toBeVisible(); // the view is visible
expect(element(by.id('YourCustomComponent.label'))).toHaveText('Some text'); // the label has some text

Repetitive components

It is highly not recommended to use non-unique testID, e.g. when your components get rendered in any sort of repeater or virtualized list:

YourScreen.jsx
const ITEMS = [
{ title: 'First Item' },
{ title: 'Second Item' },
{ title: 'Third Item' },
];

function YourScreen() {
const renderItem = ({ item }) => (
<YourCustomComponent testID={'listItem'} label={item.title} />
);

return (
<FlatList
data={ITEMS}
renderItem={renderItem}
/>
);
}

This would be a violation of accessibility guidelines and unnecessary complication for your test matchers. You’d also have to use extra matchers and .atIndex clarification:

expect(element(by.id('listItem')).atIndex(2)).toHaveText('Third Item');

Instead, you could generate a unique testID for every list item with the index property:

  const renderItem = ({ item, index }) => (
<YourCustomComponent testID={`listItem.${index + 1}`} label={item.title} />
);

That way, your assertion would become simpler and more deterministic:

expect(element(by.id('listItem.3'))).toHaveText('Third Item');

testID for repetitive components

Find your testID

note

Incorrect or absent testID is a common cause for test failure. If your test can't find your testID and you can't see it either using tools described below, that usually means you haven't passed it down to this component. Make sure you keep forwarding it down until it reaches a native component.

To make sure your testID is indeed rendered in your app, you can use such tools as "View Hierarchy" for iOS and "Layout Inspector" for Android.

  1. Build and start your app in debug mode as you usually do, e.g.:
    detox build -c ios.sim.debug
    npx react-native start
    npx react-native run-ios
  2. Open your iOS project in Xcode, e.g. YourProject/ios/YourProject.xcworkspace.
  3. Go to Debug > Attach to Process and select your app process (it is usually on top of the list). attach to debug process ios You will see a new device started with your app.
  4. Open the AppDelegate file: AppDelegate file
  5. Click Debug View Hierarchy button on the bottom panel: Debug Hierarchy button
  6. Select the component you need, and you will see your actual testID value under the Accessibility > Identifier attribute. accessibility attribute with testID value