Zum Hauptinhalt springen

React Native Testing Library: Apps richtig testen

Meistere die React Native Testing Library mit Setup, Queries, Mocking und CI-Tipps. Erstelle zuverlässige, nutzerzentrierte Tests für Komponenten, Hooks und Navigation.

React Native Testing Library: Apps richtig testen

Deine React Native Test-Suite ist grün, doch ein Nutzer meldet, dass das Tippen auf „Weiter“ auf einem echten Gerät nichts bewirkt. Der Komponententest hat den Button gefunden, den Handler aufgerufen und den erwarteten Screen in einer gemockten JavaScript-Umgebung gesehen. Er hat aber nie den nativen Berechtigungsdialog, das Tastaturverhalten, Animationen, die API der Plattform oder den tatsächlichen Navigation Stack geprüft.

Genau hier entsteht bei Teams falsche Sicherheit. React Native Testing Library ist hervorragend geeignet, um zu testen, was eine Komponente rendert und wie sie auf Nutzerinteraktionen reagiert, aber sie ist kein Ersatz für Device-Tests oder Performance-Messungen. Die zuverlässige Strategie: jede Ebene für genau die Fehler nutzen, die sie aufdecken kann.

Inhaltsverzeichnis

Warum nutzerzentriertes Testing alles verändert

Ein häufiger Testfehlschlag beginnt schon, bevor der Test geschrieben wird. Ein Entwickler prüft die Props einer Komponente, greift auf ihren State zu oder vergleicht einen großen Snapshot, weil diese Details leicht zu asserten sind. Der Test besteht, doch dann ändert ein Refactor die Komponentenstruktur, ohne das Nutzererlebnis zu verändern, und die Testsuite schlägt fehl. Schlimmer noch: Der Test kann weiterhin bestehen, obwohl das für den User sichtbare Verhalten falsch ist, weil die Assertion nie beschrieben hat, was der User sehen muss.

Die React Native Testing Library verfolgt den entgegengesetzten Ansatz. Du renderst die Komponente, interagierst über ein sichtbares Bedienelement mit ihr und assertest dann das Ergebnis, das ein User beobachten kann. Dieser Ansatz entspricht dem nutzerzentrierten Beispiel der React Native Testing Librarysowie den Testing-Guidelines von React Native: Halte Tests kurz, fokussiere jeden Test auf eine Sache, trenne View-Belange von Business-Logik und State und bevorzuge sichtbaren Output oder Accessibility-Helper gegenüber internen Implementierungsdetails.

Ein Diagramm, das die Vorteile von nutzerzentriertem Testing illustriert und dabei Nutzerverhalten, Resilienz, Zuverlässigkeit sowie Best Practices hervorhebt.

Teste das Verhalten, auf das User angewiesen sind

Angenommen, ein Login-Formular deaktiviert den Submit-Button, während eine Anfrage läuft. Ein brüchiger Test würde vielleicht prüfen disabled auf eine bestimmte Komponenteninstanz oder prüft, ob sich eine State-Variable geändert hat. Ein stärkerer Test betätigt das barrierefreie „Sign in“-Element, wartet auf den Ladeindikator und prüft, ob eine Fehlermeldung oder der Zielbildschirm erscheint.

Dem zweiten Test ist es egal, ob das Formular lokalen State, einen Reducer, einen Custom Hook oder eine andere Button-Implementierung nutzt. Ihm geht es darum, dass die App das richtige Ergebnis vermittelt.

Praktische Regel: Wenn ein Nutzer es nicht wahrnehmen kann, hinterfrage, ob es in einen Test für Komponentenverhalten gehört.

Queries auf Basis von Accessibility-Labels und Roles führen auch zu einem besseren Produkt code. Ein Screen, der aussagekräftige Labels bereitstellt, ist leichter mit assistiven Technologien zu bedienen und einfacher in Tests zu prüfen. Dieser Zusammenhang ist wichtig, wenn du die umfassendere App-User-Experiencebewertest, da Testbarkeit und Usability oft Hand in Hand gehen.

Versteht, was erfolgreiche Tests nicht beweisen

Ein benutzerzentrierter Komponententest kann beweisen, dass JavaScript den erwarteten Branch rendert und auf ein simuliertes Drücken reagiert. Er kann aber nicht beweisen, dass sich ein biometrischer Prompt korrekt öffnet, dass eine native Kamera ein nutzbares Ergebnis liefert oder dass ein Payment-Flow plattformspezifisches Lifecycle-Verhalten übersteht.

Diese Grenze ist keine Schwäche der Library. Sie ist ein Grund, die Testschichten sauber zu halten. Nutze RNTL für das Komponentenverhalten und reserviere gerätebasierte Tests für Flows, bei denen native Integration, echte Navigation, Permissions, Authentifizierung, Payments oder die Kernfunktionalität der App das Ergebnis beeinflussen können.

Installation und Konfiguration, die wirklich funktionieren

Ein modernes Setup beginnt mit dem Scoped Package:

@testing-library/react-native

Der ältere react-native-testing-library npm Package-Name existiert noch als historisches Artefakt, aber aktuelle Projekte sollten das Scoped Package verwenden, das in der Testing-Library-Familie gepflegt wird. Das Projekt GitHub Repository beschreibt es als React Native Utilities zur Förderung guter Testing-Praktiken. Die Release-Historie zeigt, dass sich die Library parallel zu React Native weiterentwickelt hat, anstatt nur ein statischer Helper zu bleiben.

Installiere die Library in deiner bestehenden Jest-Umgebung. Expo-Projekte nutzen meist das Expo-Jest-Preset, während Bare-React-Native-Projekte das React-Native-Preset verwenden können:

npm install --save-dev @testing-library/react-native jest

Für Expo füge das Preset hinzu, das dein Projekt bereits nutzt:

{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo"
  }
}

Verwende für eine Bare React Native App stattdessen die entsprechende React Native Jest Konfiguration. Achte darauf, dass das Preset zur Framework-Version passt. Viele Fehler, die wie RNTL-Probleme aussehen, kommen von einem Mismatch bei React Renderer, Babel Transform oder Jest Preset.

Haltet Setup-Dateien explizit

Packe die gemeinsame Environment-Konfiguration in eine Setup-Datei, anstatt Mocks in jedem Test zu wiederholen:

// jest.setup.js
import '@testing-library/react-native/extend-expect';

Referenziere sie dann in Jest:

{
  "jest": {
    "preset": "jest-expo",
    "setupFilesAfterEnv": ["<rootDir>/jest.setup.js"]
  }
}

Wenn deine App React Navigation, Reanimated, Gesture Handling, Safe-Area-Context oder Storage-Module nutzt, konfiguriere nur die Mocks, die deine Testumgebung braucht. Ein globaler Mock, der das App-Verhalten ändert, kann jeden Test leichter bestehen lassen, macht die Suite aber weniger vertrauenswürdig.

TypeScript braucht genauso viel Aufmerksamkeit. Stelle sicher, dass Jest .ts und .tsx Dateien über das Preset oder deine Babel-Konfiguration transformiert und halte Test-Typen für den Compiler verfügbar. Ein Test, der lokal läuft, aber nicht typgeprüft wird, kann falsche Query-Namen, ungültige Navigationsparameter oder unsichere Mock-Strukturen verbergen.

Die Release-Chronologie ist nützlich, wenn du ältere Setup-Empfehlungen einordnen willst. Das Projekt listet 127 Releases, wobei v14.0.1, getaggt am 2026-06-23, während v12.9.0, veröffentlicht am 27.11.2024, fügte offizielle Unterstützung für React Native 0.77 und Expo 52 hinzu. Die v14-Alpha-Linie im März 2025 wechselte vom deprecated React Test Renderer zum Universal Test Renderer und bereitete auf die ausschließliche React-19-Unterstützung vor. Diese Details finden sich in der RNTL-Release-Historie, also kopiere keine Renderer-Abhängigkeit aus einem veralteten Tutorial, ohne die Versionen deiner App zu prüfen.

Vergleiche für eine solide Jest-Basis deine Konfiguration mit diesem Leitfaden für Unit Tests in JavaScript, und führe dann einen kleinen Komponententest aus, bevor du Navigation und Native Mocks hinzufügst.

Eine Infografik, die den fünfstufigen Installationspfad für das Setup des React Native Testing Library Projekts zeigt.

Core APIs: Queries und Assertions erklärt

RNTL-Tests sind erst dann zuverlässig, wenn ihre Queries dem entsprechen, was ein Nutzer sehen, finden und bedienen kann. Der API ist klein, aber ein Selector, der Implementierungsdetails offenlegt, kann einen erfolgreichen Test irreführend machen.

Starte mit render:

const screen = render(<LoginForm />);

Wähle die für den Nutzer relevanteste verfügbare Query. Bevorzuge Accessibility-orientierte Queries, wenn die Komponente sie bereitstellt, nutze sichtbaren Text, wenn der Text das Verhalten bestimmt, und behalte testID dies für Fälle ohne aussagekräftigen User-Selector oder wenn ein stabiler Integration-Hook nötig ist, vor.

Eine Infografik in Pyramidenform, die die empfohlene Reihenfolge der Queries in der Testautomatisierung zeigt.

Wählt Queries nach Timing und Intention

Jede Query-Familie hat einen eigenen Zweck:

  • Synchrones Vorhandensein: Nutzt getByRole, getByText, oder eine andere getBy Query, wenn das Element bereits existieren sollte. Der Test schlägt sofort fehl, falls nicht.
  • Asynchrones Erscheinen: Nutzt findByRole oder findByText wenn das Rendering oder eine Interaktion ein asynchrones Update auslöst.
  • Prüfung auf Abwesenheit: Nutzt queryByText oder queryByTestId wenn ein Element fehlen kann und ein Null-Ergebnis statt einer Exception erwartet wird.
  • Fallback-Selektoren: Nutzt getByTestId bewusst. Es gibt komplexen Controls einen dauerhaften Hook, sollte aber nicht die barrierefreien Labels in der gesamten App ersetzen.

Für gezielte Event-Tests, fireEvent.press ist direkt:

fireEvent.press(screen.getByRole('button', { name: 'Save' }));

Nutzt userEvent wenn die installierte Version die realistischere Interaktionssequenz unterstützt. In jedem Fall die resultierende UI prüfen:

expect(await screen.findByText('Saved')).toBeTruthy();

Eine Handler-Assertion passt zu einer Komponente, deren öffentlicher Contract ein Event-Callback ist. Als alleiniger Beweis dafür, dass die User Journey funktioniert, ist sie aber zu schwach.

Nutzt spezifische Assertions statt Snapshots

Gute Assertions beschreiben den Screen:

expect(screen.getByText('Account created')).toBeTruthy();
expect(screen.getByRole('button', { name: 'Continue' })).toBeEnabled();

Sie können auch Accessibility-Status, Auswahl und sichtbares Validierungsfeedback prüfen. Vermeide es, interne Verdrahtung zu prüfen:

expect(screen.getByTestId('submit-button').props.onPress).toBeDefined();

Diese Assertion beweist nur, dass eine Prop existiert, nicht, dass die Funktion funktioniert. Kleine, gezielte Snapshots können strukturelle Änderungen aufdecken, während große Navigations- oder Screen-Snapshots oft unübersichtliche Reviews erzeugen und es leicht machen, unerklärte Updates zu genehmigen.

Das Testing-Library-Paket gehört zur größeren testing-library npm-Organisation. Zu den aktiven Paketen gehören @testing-library/react-native, mit Version 13.3.3, veröffentlicht 2026, according to the project’s Repository-Informationendes Projekts. Gemeinsame Query-Konventionen helfen plattformübergreifend, entscheiden aber nicht, welche Assertion das Verhalten Ihres Produkts abbildet.

Für einen breiteren Vergleich von Jest-Komponententest-Praktiken siehe diesen Guide zu Unit Testing von React. RNTL bleibt an der Grenze der per JavaScript gerenderten Komponenten stehen. Native Berechtigungen, echte Navigationsstapel, Geräte-Tastaturen, Frame-Timing und Speicherverhalten erfordern E2E- oder Performance-Tools statt weiterer Komponenten-Mocks.

Das folgende Video demonstriert den Query- und Assertion-Workflow im Kontext.

Praxisnahe Patterns für Components, Hooks und Navigation

Eine nützliche Test-Suite folgt der Struktur der Anwendung. Präsentationskomponenten benötigen direkte Verhaltenstests, Hooks kontrollierte Eingaben und Ausgaben, Navigation einen realistischen Provider und native Module Mocks, die sich deutlich von der Verifikation auf echten Geräten unterscheiden.

Ein moderner Laptop auf einem Holztisch, der React Native code und ein Mobile-App-Mockup anzeigt.

Presentational Components

Halte einen Komponententest nah an seinem öffentlichen Vertrag:

const onSelect = jest.fn();

render(
  <PlanCard
    title="Team"
    description="Shared workspace"
    onSelect={onSelect}
  />
);

fireEvent.press(screen.getByRole('button', { name: 'Choose Team' }));

expect(onSelect).toHaveBeenCalled();

Das exakte Label sollte mit deiner UI übereinstimmen. Der wichtige Teil ist, dass der Test das Steuerelement so findet, wie es ein Nutzer oder ein Accessibility-Dienst würde, und das sichtbare oder Callback-Ergebnis bestätigt, das zählt. Mocke nicht standardmäßig jede Kindkomponente. Mocke nur teure oder irrelevante Grenzen, wenn sie das getestete Verhalten verdecken.

Für einen Custom Hook verwende renderHook wenn die installierte RNTL-Version es bereitstellt:

const { result } = renderHook(() => useSearch());

await act(async () => {
  await result.current.submit('query');
});

expect(result.current.status).toBe('success');

Der Hook-Test sollte die Netzwerk- oder Repository-Grenze kontrollieren, statt die gesamte App nachzubauen. Teste den Screen separat, damit du siehst, ob der State des Hooks zu brauchbarer UI wird.

Für das Navigationsverhalten ist das Rendern eines Screens innerhalb eines echten NavigationContainer und eines kleinen Test-Navigators oft wertvoller, als jede Navigationsmethode zu mocken. Klicke auf ein sichtbares Steuerelement, warte auf den Inhalt des Ziel-Screens und prüfe die Ausgabe des neuen Screens. Ein direkter useNavigation Mocking ist bei einem kleinen Button, dessen einzige Aufgabe das Dispatching einer typisierten Route ist, weiterhin sinnvoll, validiert aber weder die Routenregistrierung noch Parameter noch das Verhalten verschachtelter Navigatoren.

Async-Daten verdienen dieselbe Disziplin. Mocke die API- oder Repository-Response, rendere den Screen, prüfe den Loading-State, löse den Request auf und prüfe dann den Success- oder Error-Output. Nutze findBy Queries für erwartete, eventuell erscheinende Elemente und mache abgelehnte Requests explizit; sonst kann ein Test bestehen, weil die Komponente den beabsichtigten Branch nie erreicht hat.

Mocks für Native Module

Mocks für AsyncStorage, Permissions, Kameras, Biometrie und Plattform-APIs sind nützlich für deterministische JavaScript-Tests. Sie sind jedoch kein Beweis dafür, dass das native Feature funktioniert. Halte das Mock-Verhalten nah am Modul-Contract, setze Calls zwischen den Tests zurück und simuliere Fehler-Responses statt nur den Happy Path.

Szenario Am besten mit RNTL Benötigt E2E-Validierung
Formularvalidierung und sichtbare Fehler Ja Normalerweise nicht
Loading-, Success- und Error-UI aus einem gemockten Repository Ja Für kritische Production-Flows
Navigation zwischen registrierten Screens Ja, mit einem Test-Navigator Ja, bei Gesten, Deep Links oder Plattformverhalten
Entscheidungen zum AsyncStorage-State Ja, mit einem kontrollierten Mock Ja, wenn Launch und Persistenz mit dem nativen Lifecycle interagieren
Kamera, Biometrie, Permissions oder Plattform-APIs JS-Fallback und Branching-Logik Ja, auf echten oder repräsentativen Geräten
Layout, Rendering-Performance und native code Nein Ja, mit einem Gerät oder speziellen Tools

Die Grenze ist pragmatisch: Mocke die Dependency, um deine JavaScript-Entscheidungen zu testen, und führe dann einen Gerätetest aus, um das reale Verhalten der Dependency zu bestätigen.

Debugging von Flaky Tests, CI und Performance-Checks

Ein flaky Test deutet oft auf unkontrollierte Zeit, Shared State oder eine Assertion hin, die mit der UI um Timing konkurriert. Finde heraus, welche Bedingung vorliegt, bevor du Timeouts erhöhst. Ein längeres Timeout kann das Scheduling-Problem verbergen und die Suite langsamer machen.

Nutzt findBy für Elemente, die nach einem Update erscheinen sollen. Nutze waitFor für State-Bedingungen oder Mock-Aufrufe. Wenn Fake Timer aktiviert sind, spule sie an der Stelle vor, an der die Interaktion es erfordert, und stelle danach die echten Timer wieder her. Eine act Warnung bedeutet, dass React ein Update außerhalb der erwarteten Interaktionsgrenze beobachtet hat. Behebe die fehlende await, die User-Interaktion oder den Timer-Flush, anstatt die Warnung zu unterdrücken.

CI-Failures reproduzierbar machen

Ein zuverlässiger CI-Job installiert aus dem Lockfile, nutzt denselben Jest-Befehl wie lokal und isoliert den Mock-State. Setze Mock-Aufrufe zwischen den Tests zurück, resette Module, wenn State auf Modulebene das Verhalten beeinflusst, und vermeide Abhängigkeiten von der Ausführungsreihenfolge. Jest-Caching beschleunigt das Feedback, aber Änderungen an Dependencies oder der Konfiguration erfordern eine passende Cache-Invalidierung.

CI-only-Fehler erfordern einen Umgebungsvergleich, bevor eine Komponente neu geschrieben wird. Prüfe Node, den Package Manager, die Jest-Worker-Einstellungen, die Timer-Konfiguration und die Umgebungsvariablen. Reproduziere denselben Befehl nach Möglichkeit lokal und reduziere den fehlschlagenden Test auf die kleinste Interaktion, die den Unterschied zeigt.

Observability deckt eine andere Lücke ab. App-Health-Monitoring liefert Fehlerkontext aus der Produktion, den gemockte Komponententests nicht reproduzieren können, einschließlich Fehlern im Zusammenhang mit echten Geräten und nativen Integrationen.

Sicherheitskritische Flows benötigen eine zweite Grenze. Nutze Komponententests für Validierung und State-Transitions und ergänze Device-Level-Coverage für die Übergabe und das native Verhalten. Für einmalige code Flows schau in die Anleitung, wie man SMS-Verifizierungsflows testet, wenn diese Integration Teil des Flows ist.

Performance als Messwert behandeln, nicht als Assertion

Ein funktionaler Test kann bestätigen, dass eine Liste gerendert wird. Er kann aber nicht zuverlässig feststellen, ob ein Refactor die Renderdauer oder die Renderanzahl verändert hat, weil die Testlaufzeit Rauschen erzeugt. Reassure misst diese Werte für ein Szenario, wiederholt das Szenario, um die Varianz zu reduzieren, und wendet eine statistische Analyse an, bevor eine aussagekräftige Änderung gemeldet wird. Die Performance-Testing-Dokumentation deckt auch das Reporting für CI und Pull-Request-Reviews ab.

Vermeidet feste Assertions wie „Dieses Render muss unter einem bestimmten Schwellenwert liegen“. Ein ausgelasteter CI-Worker kann zu einem falschen Fehler führen, während ein zu lockerer Schwellenwert echte Regressionen übersehen kann. Nutzt wiederholte Messungen für Regressionssignale und prüft das Komponente- und Geräteprofil, wenn der Vergleich einen signifikanten Unterschied zeigt.

Measurement rule: Funktionale Tests beantworten, ob das Verhalten korrekt ist. Performance-Tools beantworten, ob sich ein gemessenes Szenario geändert hat. Haltet diese Fragen getrennt.

Alles zusammenführen und vorankommen

React Native Testing Library gehört in die breite, schnelle Schicht eurer Teststrategie. Sie sollte das Komponentenverhalten, sichtbare Zustandsänderungen, Accessibility-Ergebnisse, Validierung, gemockte Datenzustände und die Integration zwischen JavaScript-Komponenten abdecken. Haltet diese Tests auf das beschränkt, was ein Nutzer beobachten kann, und sorgt dafür, dass Fehler auf ein spezifisches Verhalten hinweisen und nicht auf einen großen gerenderten Baum.

Die kleinere gerätebasierte Schicht sollte die Flows schützen, in denen Mocks lügen können. React Natives Testing-Übersicht dass RNTL keine vollständige React-Native-Runtime bereitstellt und native Features nicht testen kann. Die gleiche Empfehlung rät, Komponententests mit E2E-Tools wie Detox für kritische Flows zu kombinieren, einschließlich Authentifizierung, Zahlungen und Kernfunktionalitäten der App.

Ein praktischer Migrationspfad

Du musst eine bestehende Test-Suite nicht auf einmal komplett umschreiben.

  1. Behalte wertvolle Business-Tests bei. Verschiebe reine State- und Domain-Logik in fokussierte Unit-Tests, wo sie klares Feedback liefert.
  2. Ersetze zuerst Implementierungs-Assertions. Wandle Prop- und State-Checks in sichtbare Outputs, Accessibility-States und Interaktionsergebnisse um.
  3. Snapshots reduzieren. Behalte nur Snapshots, die Reviewer verstehen und pflegen können.
  4. Coverage für native Grenzen hinzufügen. Identifiziere für jedes wichtige Mock-Modul das Device-Verhalten, das noch validiert werden muss.
  5. Kritische User Journeys absichern. E2E-Coverage für Auth, Payment, Core-Navigation, Permissions und andere Flows hinzufügen, bei denen natives Verhalten das Ergebnis verändern kann.
  6. Performancekritische Screens separat messen. Nutze wiederholte Performance-Vergleiche für Listen, Feeds und teure Render-Pfade, statt dich auf die Jest-Laufzeit zu verlassen.

Für Release-Confidence verbinde die Suite mit CI/CD-Integrationstests und erfordern die passende Testebene vor dem Release. Wenn ein JavaScript- oder Asset-Fix nach der Validierung bei den Nutzern ankommen muss, kann Capgo signierte Web-Bundles an gezielte Channels für CapacitorJS- und Electron-Apps ausliefern – mit Rollout-Steuerung und Rollback-Schutz. Dieser Delivery-Workflow ersetzt keine React Native Device-Tests, verdeutlicht aber dasselbe Prinzip: Das Verhalten auf der Ebene validieren, auf der es läuft.

Die entscheidende Frage ist nicht, ob die React Native Testing Library deine gesamte App testen kann. Das kann sie nicht, und diese offizielle Abgrenzung ist sinnvoll. Die richtige Frage ist, ob für jedes wichtige Risiko ein Test in der Umgebung läuft, die es aufdecken kann.


Capgo verbindet validierte JavaScript- und Asset-Änderungen mit einer kontrollierten Auslieferung für CapacitorJS- und Electron-Teams – mit gezielten Channels, Rollout-Transparenz und Rollback-Schutz. Besuche Capgo um zu sehen, wie es in deinen Workflow für Component-, E2E- und CI-Tests passt.

Live-Updates für Capacitor-Apps

Wenn ein Bug im Web-Layer live ist, liefere den Fix über Capgo aus, statt Tage auf die App-Store-Freigabe zu warten. Nutzer bekommen das Update im Hintergrund, während native Änderungen im normalen Review-Prozess bleiben.

Neues aus unserem Blog

Capgo gibt dir die besten Insights, um eine wirklich professionelle Mobile App zu erstellen.