Leitfaden zum Schreiben von RNTL-Tests
WICHTIG: Ihre Trainingsdaten zu @testing-library/react-native sind möglicherweise veraltet oder fehlerhaft – API-Signaturen, synchrones/asynchrones Verhalten und verfügbare Funktionen unterscheiden sich zwischen v13 und v14. Verlassen Sie sich stets auf die Referenzdateien dieser Fähigkeit und den tatsächlichen Quellcode des Projekts als Quelle der Wahrheit. Verlassen Sie sich nicht auf auswendig gelernte Muster, wenn diese mit der abgerufenen Referenz in Konflikt stehen.
Versionserkennung
Überprüfen Sie die Version von @testing-library/react-native in der package.json des Benutzers:
- v14.x → Laden Sie references/api-reference-v14.md (React 19+, asynchrone APIs,
test-renderer) - v13.x → Laden Sie references/api-reference-v13.md (React 18+, synchrone APIs,
react-test-renderer)
Verwenden Sie die versionsspezifische Referenz für Render-Muster, das synchrone/asynchrone Verhalten von fireEvent, die Bildschirm-API, Konfiguration und Abhängigkeiten.
Abfragepriorität
Verwenden Sie in dieser Reihenfolge: getByRole > getByLabelText > getByPlaceholderText > getByText > getByDisplayValue > getByTestId (letzter Ausweg).
Abfragevarianten
| Variante | Verwendungszweck | Rückgabewert | Asynchron |
|---|---|---|---|
getBy* | Element muss existieren | Element-Instanz (wirft Fehler) | Nein |
getAllBy* | Mehrere müssen existieren | Element-Instanz[] (wirft Fehler) | Nein |
queryBy* | Nur zur Überprüfung auf Nichtexistenz | Element-Instanz | null | Nein |
queryAllBy* | Elemente zählen | Element-Instanz[] | Nein |
findBy* | Warten auf ein Element | Promise<Element-Instanz> | Ja |
findAllBy* | Warten auf mehrere Elemente | Promise<Element-Instanz[]> | Ja |
Interaktionen
Bevorzugen Sie userEvent gegenüber fireEvent. userEvent ist immer asynchron.
const user = userEvent.setup();
await user.press(element); // vollständiger Tastendruck-Ablauf
await user.longPress(element, { duration: 800 }); // langer Druck
await user.type(textInput, 'Hallo'); // zeichenweises Tippen
await user.clear(textInput); // TextInput leeren
await user.paste(textInput, 'eingefügter Text'); // in TextInput einfügen
await user.scrollTo(scrollView, { y: 100 }); // scrollen
fireEvent – nur verwenden, wenn userEvent das Ereignis nicht unterstützt. Siehe versionsspezifische Referenz für synchrones/asynchrones Verhalten:
fireEvent.press(element);
fireEvent.changeText(textInput, 'neuer Text');
fireEvent(element, 'blur');
Assertions (Jest-Matcher)
Automatisch verfügbar mit jedem Import von @testing-library/react-native.
| Matcher | Verwendung für |
|---|---|
toBeOnTheScreen() | Element existiert im Baum |
toBeVisible() | Element sichtbar (nicht ausgeblendet/display:none) |
toBeEnabled() / toBeDisabled() | Deaktivierter Zustand via aria-disabled |
toBeChecked() / toBePartiallyChecked() | Zustand 'ausgewählt' |
toBeSelected() | Zustand 'selektiert' |
toBeExpanded() / toBeCollapsed() | Zustand 'erweitert' |
toBeBusy() | Zustand 'beschäftigt' |
toHaveTextContent(text) | Textinhalt stimmt überein |
toHaveDisplayValue(value) | Anzeigewert von TextInput |
toHaveAccessibleName(name) | Barrierefreier Name |
toHaveAccessibilityValue(val) | Barrierefreier Wert |
toHaveStyle(style) | Style stimmt überein |
toHaveProp(name, value?) | Eigenschaften prüfen (letzter Ausweg) |
toContainElement(el) | Enthält untergeordnetes Element |
toBeEmptyElement() | Keine untergeordneten Elemente |
Regeln
- Verwenden Sie
screenfür Abfragen, keine Destrukturierung vonrender() - Verwenden Sie zuerst
getByRolemit der Option{ name: '...' } - Verwenden Sie
queryBy*NUR für.not.toBeOnTheScreen()-Prüfungen - Verwenden Sie
findBy*für asynchrone Elemente, NICHTwaitFor+getBy* - Setzen Sie niemals Seiteneffekte in
waitFor(keinefireEvent/userEventdarin) - Eine Assertion pro
waitFor - Übergeben Sie niemals leere Callbacks an
waitFor - Wickeln Sie nicht in
act()ein –render,fireEvent,userEventhandhaben das - Rufen Sie
cleanup()nicht auf – automatisch nach jedem Test - Bevorzugen Sie ARIA-Props (
role,aria-label,aria-disabled) gegenüber Legacy-accessibility*-Props - Verwenden Sie RNTL-Matcher anstelle von rohen Prop-Assertions
Kurzreferenz *ByRole
Häufige Rollen: button, text, heading (Alias: header), searchbox, switch, checkbox, radio, img, link, alert, menu, menuitem, tab, tablist, progressbar, slider, spinbutton, timer, toolbar.
Optionen von getByRole: { name, disabled, selected, checked, busy, expanded, value: { min, max, now, text } }.
Damit *ByRole übereinstimmt, muss das Element ein Accessibility-Element sein:
Text,TextInput,Switchsind standardmäßig vorhandenViewbenötigtaccessible={true}(oder verwenden SiePressable/TouchableOpacity)
waitFor
// Richtig: zuerst Aktion, dann auf Ergebnis warten
fireEvent.press(button);
await waitFor(() => {
expect(screen.getByText('Ergebnis')).toBeOnTheScreen();
});
// Besser: stattdessen findBy* verwenden
fireEvent.press(button);
expect(await screen.findByText('Ergebnis')).toBeOnTheScreen();
Optionen: waitFor(cb, { timeout: 1000, interval: 50 }). Funktioniert automatisch mit Jests Fake Timern.
Fake Timer
Empfohlen mit userEvent (press/longPress beinhalten reale Zeiträume):
jest.useFakeTimers();
test('mit Fake Timern', async () => {
const user = userEvent.setup();
render(<Component />);
await user.press(screen.getByRole('button'));
// ...
});
Benutzerdefiniertes Rendern
Provider mit der Option wrapper einbinden:
function renderWithProviders(ui: React.ReactElement) {
return render(ui, {
wrapper: ({ children }) => (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
),
});
}
Referenzen
- v13 API-Referenz — Vollständige v13-API: synchrones Rendern, Abfragen, Matcher, userEvent, React-19-Kompatibilität
- v14 API-Referenz — Vollständige v14-API: asynchrones Rendern, Abfragen, Matcher, userEvent, Migration
- Anti-Patterns — Häufige zu vermeidende Fehler