In React Native Testing Library, how should a test find a login form's email and password TextInputs, given TextInput has no default role?
answer
- priority: role, input queries, accessible, testID
- no textbox role for TextInput
- ByLabelText reads accessibilityLabel or aria-label
- labelledby resolves a nativeID
- ByTestId is the last resort
basics
~10 sTextInput has no default role, so after *ByRole the RNTL priority puts the text-input queries: getByLabelText first, matching accessibilityLabel, aria-label or a labelled-by nativeID, then getByPlaceholderText and getByDisplayValue. getByTestId stays the last resort.
solid answer
~40 sReact Native Testing Library's priority is: `*ByRole` first, then the **text input queries**, then other accessible queries (`*ByText`, `*ByLabelText`, `*ByHintText`), and `*ByTestId` last. Inputs get their own tier because React Native has no general text-field role: `searchbox` exists but only suits search fields, so `getByRole` cannot reach an email field. Within that tier I prefer `getByLabelText('Email')`, which matches `aria-label` or `accessibilityLabel`, or the text of an element whose `nativeID` is referenced by `aria-labelledby` or `accessibilityLabelledBy`; it is what a screen reader announces. `getByPlaceholderText` and `getByDisplayValue` (which matches `value` or `defaultValue`) only match `TextInput`s and are weaker, because a placeholder is not a label and a value changes. A test ID is acceptable only when nothing user-facing identifies the field.
code
tsx · 20 linesimport { Text, TextInput, View } from 'react-native';
import { render, screen } from '@testing-library/react-native';
function CredentialsFields() {
return (
<View>
<Text nativeID="emailLabel">Email</Text>
<TextInput aria-labelledby="emailLabel" placeholder="[email protected]" />
<TextInput accessibilityLabel="Password" secureTextEntry />
</View>
);
}
test('finds inputs by their labels', async () => {
await render(<CredentialsFields />);
expect(screen.getByLabelText('Email')).toBeOnTheScreen(); // via nativeID
expect(screen.getByLabelText('Password')).toBeOnTheScreen();
expect(screen.getByPlaceholderText('[email protected]')).toBeOnTheScreen(); // weaker
});go deeper
Recall the order: role first, then label, placeholder and display value for inputs, then text and hint, and test ID last.
Explain why TextInput has its own tier, what getByLabelText resolves, including nativeID references, and why placeholder and display value are weaker.
Use label-based queries to catch unlabelled fields, review suites for test-ID overuse, and reserve test IDs for elements without a user-facing identity.
Define how the design system exposes labels and roles so that component tests, screen-reader behaviour and end-to-end locators stay consistent across the app.
## The priority RNTL recommends React Native Testing Library documents a four-tier order for choosing a query **predicate**: 1. **`*ByRole`**, narrowed with `name` and state options; 2. **text input queries**: `*ByLabelText`, `*ByPlaceholderText`, `*ByDisplayValue`; 3. **other accessible queries**: `*ByText`, `*ByLabelText`, `*ByHintText`; 4. **`*ByTestId`**, as the final resort. Tier 2 is specific to React Native. On the web a text input has an implicit role that a role query can reach. A React Native `TextInput` has none: RNTL reports its role as `none` unless one is set, and the only fitting role, `searchbox` (alias `search`), is meant for search fields. So the email and password fields of a login form cannot be found with `getByRole` unless the app misuses a role. ## The text input queries compared | Query | Matches | Elements | Strength | |---|---|---|---| | `getByLabelText('Email')` | `aria-label`, `accessibilityLabel`, text of the element referenced by `aria-labelledby` / `accessibilityLabelledBy`, `Image` `alt` | any host element | What assistive technology announces; also verifies the field is labelled | | `getByPlaceholderText('[email protected]')` | `placeholder` | `TextInput` only | Visible hint, but not a label and gone once the user types | | `getByDisplayValue('[email protected]')` | `value` or `defaultValue` | `TextInput` only | Useful to assert what is filled in; fragile as a locator | The labelled-by form suits forms whose visible label is a separate `Text`: give that `Text` a `nativeID` such as `emailLabel` and the input `aria-labelledby="emailLabel"`. RNTL resolves the reference and matches the label's text. React Native's documentation marks `aria-labelledby` and `accessibilityLabelledBy` as Android props, but RNTL resolves them in tests regardless of platform; when `accessibilityLabelledBy` holds an array of IDs, their texts are joined with spaces. ## Tier 3 and 4 on the same screen - `getByText('Forgot password?')` finds a link-like `Text`; if it is a real control, prefer `getByRole('link', { name: 'Forgot password?' })`. - `getByHintText('Signs in with your saved account')` matches `accessibilityHint`; hints are secondary descriptions, so this is rarely the primary locator. The same query is also exported as `getByA11yHint` and `getByAccessibilityHint`. - `getByTestId('login-email')` matches the `testID` prop. Users never see a test ID, so a test built on it keeps passing when the label disappears. Test IDs are common in device-level end-to-end tools, which cannot always reach elements by accessibility; in component tests they are the exception. ## A login form test in this order 1. Find the fields with `getByLabelText('Email')` and `getByLabelText('Password')`. 2. Find the submit control with `getByRole('button', { name: 'Sign in' })`. 3. After interaction, find the error with `getByRole('alert', { name: /incorrect password/i })`. 4. Reach for `getByTestId` only for elements with no user-facing identity, such as a decorative container. ## Why the order matters - **Resilience**: labels and roles change when the product changes, not when a developer refactors markup. - **Accessibility as a side effect**: a field that cannot be found by label is also unlabelled for screen-reader users, so the test fails for a real reason. - **Readable failures**: `Unable to find an element with accessibility label: Email` points straight at the defect.
- When is getByDisplayValue the right query?When the assertion is about the value itself, for example that a remembered email is prefilled. It matches a `TextInput`'s `value` or `defaultValue`. As a locator it is fragile, because the value changes as the user types.
- A designer removes the visible 'Email' label and keeps only the placeholder. What should the test reveal?If the test uses `getByLabelText('Email')` and the input had no separate accessibility label, it now fails, correctly: the field has lost its label for screen-reader users. A test using `getByPlaceholderText` or `getByTestId` would stay green and hide the regression.
saying these in an interview costs you the question
- getByRole('textbox') finds any React Native TextInput.
- getByPlaceholderText is as good as a label for locating fields.
- getByLabelText only reads the accessibilityLabel prop.
- Test IDs are the preferred locator in component tests because they are stable.
- getByText can locate a TextInput by the text typed into it.