Zum Hauptinhalt springen

React Native Testing Library: So testet man Apps richtig

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

React Native Testing Library: So testet man Apps richtig

Deine React Native Test-Suite ist grün, doch ein User meldet, dass das Tippen auf „Continue“ auf einem echten Device nichts bewirkt. Der Component-Test hat den Button gefunden, den Handler aufgerufen und den erwarteten Screen in einer gemockten JavaScript-Umgebung gesehen. Er hat jedoch nie den nativen Permission Prompt, das Tastaturverhalten, Animationen, die API Plattform oder den tatsächlichen Navigation Stack verifiziert.

Genau diese Lücke führt zu falscher Sicherheit in Teams. React Native Testing Library ist hervorragend geeignet, um zu testen, was eine Component rendert und wie sie auf User-Interaktionen reagiert, aber sie ist kein Ersatz für Device-Tests oder Performance-Messungen. Die zuverlässige Strategie ist, jede Layer für die Fehler zu nutzen, die sie aufdecken kann.

Inhaltsverzeichnis

Warum nutzerzentriertes Testen alles verändert

Ein häufiger Testfehler entsteht schon vor dem Schreiben des Tests. Ein Entwickler prüft die Props einer Komponente, greift auf ihren State zu oder vergleicht einen großen Snapshot, weil sich diese Details leicht als Assertion formulieren lassen. Der Test besteht, doch ein Refactor ändert die Struktur der Komponente, ohne das Nutzererlebnis zu beeinflussen – und die Testsuite schlägt fehl. Schlimmer noch: Der Test kann weiterhin bestehen bleiben, obwohl das für den Nutzer sichtbare Verhalten falsch ist, weil die Assertion nie beschrieben hat, was der Nutzer tatsächlich sehen muss.

React Native Testing Library verfolgt den entgegengesetzten Ansatz. Du renderst die Komponente, interagierst über ein sichtbares Element mit ihr und prüfst dann das Ergebnis, das ein Nutzer beobachten kann. Dieser Ansatz passt zum nutzerzentrierten Beispiel von React Native Testing Librarysowie zu den Testing-Guidelines von React Native: Tests kurz halten, jeden Test auf eine Sache fokussieren, View-Belange von Business-Logik und State trennen und sichtbare Outputs oder Accessibility-Helper gegenüber internen Implementierungsdetails bevorzugen.

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

Test the behavior users depend on

Angenommen, ein Login-Formular deaktiviert den Submit-Button, während eine Anfrage läuft. Ein instabiler Test würde vielleicht prüfen disabled auf eine bestimmte Komponenten-Instanz oder prüft, ob sich eine State-Variable geändert hat. Ein stärkerer Test drückt das zugängliche „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. Er stellt nur sicher, dass die App das richtige Ergebnis liefert.

Praktische Regel: Wenn ein Nutzer es nicht beobachten kann, sollte man hinterfragen, ob es Teil eines Component-Behavior-Tests sein sollte.

Queries basierend auf Accessibility-Labels und Roles fördern zudem ein besseres Produkt code. Ein Screen mit aussagekräftigen Labels lässt sich leichter mit assistiven Technologien bedienen und einfacher in Tests prüfen. Dieser Zusammenhang ist wichtig, wenn man das gesamte App-User-Experience, denn Testbarkeit und Usability verbessern sich oft gemeinsam.

Was erfolgreiche Tests nicht beweisen können

Ein nutzerzentrierter Komponententest kann beweisen, dass JavaScript den erwarteten Zweig rendert und auf ein simuliertes Press reagiert. Er kann jedoch nicht beweisen, dass 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 trennen. Nutze RNTL für das Komponenten-Verhalten und reserviere device-basierte 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 funktioniert

Das moderne Setup beginnt mit dem Scoped Package:

@testing-library/react-native

Der ältere react-native-testing-library npm Paketname existiert zwar noch als historisches Artefakt, aber aktuelle Projekte sollten das Scoped Package nutzen, das innerhalb 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 stetig mit React Native weiterentwickelt, statt 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, entstehen durch einen mismatch beim React Renderer, Babel Transform oder Jest Preset.

Setup-Dateien explizit halten

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 wirklich braucht. Ein globaler Mock, der das App-Verhalten verändert, lässt zwar jeden Test leichter durchlaufen, macht die gesamte Suite aber weniger zuverlässig.

TypeScript erfordert die gleiche Aufmerksamkeit. Stelle sicher, dass Jest die .ts und .tsx Dateien über das Preset oder deine Babel-Konfiguration transformiert und halte die Test-Typen für den Compiler verfügbar. Ein Test, der lokal läuft, aber nicht per Type-Check geprüft wird, kann falsche Query-Namen, ungültige Navigationsparameter oder unsichere Mock-Shapes verbergen.

Die Release-Chronologie ist hilfreich, um ältere Setup-Anleitungen einzuordnen. Das Projekt listet 127 Releases, mit v14.0.1 getaggt am 23.06.2026, während v12.9.0, veröffentlicht am 27.11.2024, fügte offiziellen Support für React Native 0.77 und Expo 52 hinzu. Die v14-Alpha-Linie im März 2025 wechselte vom veralteten React Test Renderer zum Universal Test Renderer und bereitete den Support für ausschließlich React 19 vor. Diese Details findest du in der RNTL Release History, 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 JavaScript Unit Tests, dann führe einen kleinen Komponententest aus, bevor du Navigation und native Mocks hinzufügst.

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

Core APIs: Queries und Assertions erklärt

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

Starte mit render:

const screen = render(<LoginForm />);

Wähle die für den User relevanteste verfügbare Query. Bevorzuge Accessibility-orientierte Queries, wenn die Komponente diese bietet, nutze sichtbaren Text, wenn der Text das Verhalten bestimmt, und reserviere testID für Fälle ohne aussagekräftigen User-Selector oder wenn ein stabiler Integration-Hook benötigt wird.

Ein Infografik-Pyramidendiagramm, das die empfohlene Prioritätenfolge für Queries in der Testautomatisierung zeigt.

Wähle die Query nach Timing und Intention

Jede Query-Familie hat einen eigenen Zweck:

  • Synchrones Vorhandensein: Nutze getByRole, getByText, oder eine andere getBy Query, wenn das Element bereits existieren sollte. Der Test schlägt sofort fehl, falls nicht.
  • Asynchrones Erscheinen: Nutze findByRole oder findByText wenn das Rendering oder eine Interaktion ein asynchrones Update auslöst.
  • Prüfung auf Abwesenheit: Nutze queryByText oder queryByTestId wenn ein Element fehlen kann und ein Null-Ergebnis statt einer Exception erwartet wird.
  • Fallback-Selektoren: Nutze getByTestId bewusst. Es bietet komplexen Steuerelementen einen stabilen Anker, 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' }));

Nutze 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 eignet sich für Komponenten, deren öffentlicher Contract ein Event-Callback ist. Als alleiniger Beweis für den User Journey ist sie jedoch zu schwach.

Spezifische Assertions statt Snapshots bevorzugen

Gute Assertions beschreiben den Screen:

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

Sie können auch den Accessibility-Status, die Auswahl und sichtbares Validierungs-Feedback prüfen. Vermeiden Sie es, die interne Logik zu testen:

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

Diese Assertion beweist nur, dass ein Prop existiert, nicht dass die Funktion arbeitet. Kleine, gezielte Snapshots können strukturelle Änderungen erkennen, während große Navigations- oder Screen-Snapshots oft zu verrauschten Reviews führen und unerklärte Updates zu leicht machen.

Das Testing Library Paket gehört zur größeren testing-library npm Organisation. Zu den aktiven Paketen gehören @testing-library/react-native, mit der Version 13.3.3 veröffentlicht im Jahr 2026, gemäß dem Projekt- Repository-Informationen des Projekts. Gemeinsame Query-Konventionen helfen plattformübergreifend, entscheiden aber nicht darüber, welche Assertion das Verhalten deines Produkts korrekt abbildet.

Für einen breiteren Vergleich von Jest Component-Testing-Praktiken sieh dir diesen Guide zu Unit Testing in React an. RNTL endet weiterhin an der Grenze der JavaScript-gerenderten Komponenten. Native Permissions, echte Navigation-Stacks, Device-Keyboards, Frame-Timing und Memory-Verhalten benötigen E2E- oder Performance-Tools statt noch mehr Component-Mocks.

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

Praktische Patterns für Komponenten, Hooks und Navigation

Eine nützliche Testsuite folgt der Struktur der App. Presentational Components benötigen direkte Verhaltenstests, Hooks kontrollierte Inputs und Outputs, die Navigation einen realistischen Provider und Native Modules Mocks, die sich deutlich von der echten Device-Verifizierung unterscheiden.

Ein moderner Laptop auf einem Holztisch mit React Native code und einem Mobile-App-Mockup.

Presentational Components

Halte Komponententests nah an ihrem öffentlichen Contract:

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 Label sollte exakt mit deinem UI übereinstimmen. Wichtig ist, dass der Test das Steuerelement so findet, wie es ein User oder ein Accessibility-Service tun würde, und das sichtbare Ergebnis oder den Callback bestätigt. Mocke nicht standardmäßig jede Child-Komponente. Mocke teure oder irrelevante Boundaries nur dann, wenn sie das zu testende Verhalten verdecken.

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

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

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

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

Der Hook-Test sollte die Network- oder Repository-Boundary kontrollieren, nicht die gesamte App reproduzieren. Teste den Screen separat, damit du weißt, ob der State des Hooks zu einem nützlichen UI führt.

Für das Navigationsverhalten ist das Rendern eines Screens in einem echten NavigationContainer und ein kleiner Test-Navigator oft wertvoller, als jede Navigationsmethode zu mocken. Drücke ein sichtbares Steuerelement, warte auf den Inhalt des Ziel-Screens und prüfe dessen Output. Ein direkter useNavigation Mocking ist für einen kleinen Button, der nur eine typisierte Route dispatched, noch angemessen; es validiert jedoch weder die Routenregistrierung noch Parameter oder das Verhalten verschachtelter Navigatoren.

Async-Daten erfordern 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 Elemente und mache abgelehnte Requests explizit, sonst besteht ein Test eventuell nur deshalb, weil die Komponente den beabsichtigten Code-Zweig nie erreicht hat.

Native Module Mocks

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

Szenario Bestens 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 Plattform-Verhalten
AsyncStorage-State-Entscheidungen Ja, mit einem kontrollierten Mock Ja, wenn Launch und Persistence mit dem Native 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 spezialisierten 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 die Wette läuft. Finde erst heraus, welche Bedingung vorliegt, bevor du Timeouts erhöhst. Ein längeres Timeout kann das Scheduling-Problem verdecken und die Suite langsamer machen.

Nutze findBy für Elemente, die nach einem Update erscheinen sollten. 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 registriert hat. Behebe das fehlende await, User-Interaktion oder Timer-Flush, anstatt die Warnung zu unterdrücken.

CI-Failures reproduzierbar machen

Ein zuverlässiger CI-Job installiert aus dem Lockfile, führt denselben Jest-Befehl aus 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.

Fehler, die nur in der CI auftreten, erfordern einen Umgebungsvariablen-Vergleich, bevor Komponenten neu geschrieben werden. Prüfe Node, den Package Manager, Jest-Worker-Einstellungen, Timer-Konfigurationen und Umgebungsvariablen. Versuche, den Befehl lokal zu reproduzieren und reduziere den fehlerhaften Test auf die kleinste Interaktion, die den Unterschied aufzeigt.

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

Sicherheitskritische Flows benötigen eine zweite Barriere. Nutze Komponententests für Validierungen und State-Transitions, und ergänze die Abdeckung auf Geräteebene für den Übergang und das native Verhalten. Für einmalige code Flows schau dir an, wie man SMS-Verifizierung-Flows testet, wenn diese Integration Teil des Flows ist.

Betrachte Performance als Messwert, nicht als Assertion

Ein funktionaler Test kann bestätigen, dass eine Liste gerendert wird. Er kann jedoch nicht zuverlässig feststellen, ob ein Refactor die Render-Dauer oder die Anzahl der Renders verändert hat, da die Test-Laufzeit Rauschen hinzufügt. Reassure misst diese Werte für ein Szenario, wiederholt das Szenario zur Varianzreduktion und wendet statistische Analysen an, bevor eine sinnvolle Änderung gemeldet wird. Die Performance-Testing-Dokumentation deckt auch das Reporting für CI und Pull-Request-Reviews ab.

Vermeide feste Assertions wie „dieser Render muss unter einem bestimmten Schwellenwert liegen“. Ein ausgelasteter CI-Worker kann Fehlalarme auslösen, während ein zu lockerer Schwellenwert echte Regressionen übersieht. Nutze wiederholte Messungen für Regressionssignale und analysiere Komponente sowie Device-Profil, wenn der Vergleich signifikante Unterschiede zeigt.

Measurement rule: Funktionale Tests klären, ob das Verhalten korrekt ist. Performance-Tools klären, ob sich ein gemessener Szenario verändert hat. Trenne diese Fragestellungen strikt.

Alles zusammenführen und der nächste Schritt

Die React Native Testing Library gehört in die schnelle, breite Schicht deiner Teststrategie. Sie sollte Komponentenverhalten, sichtbare State-Änderungen, Accessibility, Validierungen, Mock-Datenzustände und die Integration zwischen JS-Komponenten abdecken. Konzentriere diese Tests auf das, was ein User beobachten kann, damit Fehler auf ein spezifisches Verhalten hinweisen statt auf einen riesigen Render-Tree.

Die kleinere, device-basierte Schicht sollte die Flows absichern, bei denen Mocks täuschen können. React Native Test-Übersicht weist darauf hin, dass RNTL keine vollständige React Native Runtime bietet und keine nativen Features testen kann. Es wird empfohlen, Komponententests mit E2E-Tools wie Detox für kritische Flows wie Authentifizierung, Zahlungen und Kernfunktionen der App zu kombinieren.

Ein praktischer Migrationspfad

Du musst eine bestehende Suite nicht in einem Rutsch umschreiben.

  1. Behalte wertvolle Business-Tests bei. Verschiebe reine State- und Domain-Logik in fokussierte Unit-Tests, um klares Feedback zu erhalten.
  2. Ersetze zuerst Assertions auf die Implementierung. Wandle Prop- und State-Checks in sichtbare Outputs, Accessibility-States und Interaktionsergebnisse um.
  3. Snapshots verkleinern. Behalte nur Snapshots, die Reviewer verstehen und warten können.
  4. Native Boundary Coverage hinzufügen. Identifiziere bei jedem wichtigen Mock-Modul das Geräteverhalten, 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 beeinflusst.
  6. Sensitive Screens separat messen. Nutze wiederholte Performance-Vergleiche für Listen, Feeds und teure Render-Pfade, statt dich auf die Jest-Dauer zu verlassen.

Für Release-Confidence verbinde die Suite mit CI/CD Integration Testing und erfordert die entsprechende Testschicht vor dem Release. Wenn ein JavaScript- oder Asset-Fix nach der Validierung die Nutzer erreichen muss, kann Capgo signierte Web-Bundles an gezielte Channels für CapacitorJS- und Electron-Apps ausliefern – inklusive 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 tatsächlich 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 kritische Risiko ein Test in der Umgebung läuft, die dieses Risiko auch tatsächlich 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, rollst du den Fix über Capgo aus, statt Tage auf die App-Store-Freigabe zu warten. Nutzer bekommen das Update im Hintergrund, während native Änderungen weiter den normalen Review-Prozess durchlaufen.

Neuestes aus unserem Blog

Capgo bietet dir die besten Insights für eine wirklich professionelle Mobile App.