Aller au contenu principal

React Native Testing Library : comment tester vos apps correctement

Maîtrisez React Native Testing Library : configuration, requêtes, mocking et astuces CI. Créez des tests fiables centrés sur l’utilisateur pour vos composants, hooks et navigation.

React Native Testing Library : comment tester vos apps correctement

Votre suite de tests React Native est verte, mais un utilisateur signale encore que le bouton « Continuer » ne fait rien sur un appareil réel. Le test du composant a trouvé le bouton, appelé son handler et vu l’écran attendu dans un environnement JavaScript mocké. Il n’a jamais vérifié le prompt de permission natif, le comportement du clavier, l’animation, la plateforme API ou la pile de navigation réelle.

C’est dans cet écart que les équipes se trompent de confiance. React Native Testing Library est excellente pour tester ce qu’un composant affiche et comment il réagit aux interactions utilisateur, mais elle ne remplace ni les tests sur appareil ni la mesure des performances. La stratégie fiable consiste à utiliser chaque couche pour les échecs qu’elle peut révéler.

Table des matières

Pourquoi les tests centrés sur l'utilisateur changent tout

Un échec de test classique survient avant même l'écriture du test. Un développeur inspecte les props d'un composant, accède à son état ou compare un snapshot volumineux parce que ces détails sont faciles à vérifier. Le test passe, puis un refactoring modifie la structure du composant sans changer l'expérience, et la suite de tests casse. Pire encore, le test peut continuer à passer alors que le comportement visible par l'utilisateur est erroné, car l'assertion n'a jamais décrit ce que l'utilisateur doit voir.

React Native Testing Library adopte l'approche inverse. Vous rendez le composant, interagissez avec lui via un contrôle visible, puis vérifiez le résultat observable par l'utilisateur. Cette approche correspond L'exemple centré sur l'utilisateur de React Native Testing Library, ainsi qu'aux recommandations de test de React Native : garder des tests courts, concentrer chaque test sur un seul point, séparer les préoccupations de vue de la logique métier et de l'état, et privilégier le rendu visible ou les helpers d'accessibilité plutôt que les détails d'implémentation interne.

Un diagramme illustrant les avantages des tests centrés sur l'utilisateur, mettant en avant le comportement utilisateur, la résilience, la fiabilité et les meilleures pratiques.

Test the behavior users depend on

Supposons qu'un formulaire de connexion désactive son bouton de validation pendant une requête. Un test fragile pourrait inspecter disabled sur une instance de composant donnée ou vérifie qu'une variable d'état a changé. Un test plus robuste simule l'appui sur le contrôl’accessible « Sign in », attend l'indicateur de chargement et vérifie qu'un message d'erreur ou l'écran de destination apparaît.

Le second test ne se soucie pas de savoir si le formulaire utilise un état local, un reducer, un hook personnalisé ou une autre implémentation de bouton. Il vérifie que l'app communique le bon résultat.

Règle pratique : Si l'utilisateur ne peut pas l'observer, demandez-vous si cela a sa place dans un test de comportement de composant.

Les requêtes basées sur les rôles et les labels d'accessibilité imposent aussi une meilleure code produit. Un écran qui expose des labels explicites est plus facile à utiliser avec les technologies d'assistance et plus simple à tester. Ce lien compte lorsque vous évaluez l'aspect global expérience utilisateur d'application cohérente, car la testabilité et l'utilisabilité s'améliorent souvent de concert.

Comprenez ce que des tests réussis ne prouvent pas

Un test de composant centré sur l'utilisateur peut prouver que le JavaScript rend la branche attendue et répond à un appui simulé. Il ne peut pas prouver qu'un prompt biométrique s'ouvre correctement, qu'une caméra native renvoie un résultat exploitable ou qu'un flux de paiement survit au comportement de cycle de vie spécifique à la plateforme.

Cette limite n'est pas une faiblesse de la bibliothèque. C'est une raison de garder les couches de test honnêtes. Utilisez RNTL pour le comportement des composants, puis réservez les tests sur appareil aux flux où l'intégration native, la navigation réelle, les permissions, l'authentification, les paiements ou les fonctionnalités clés de l'app peuvent changer le résultat.

Installation et configuration qui fonctionnent vraiment

La configuration moderne commence par le package scopé :

@testing-library/react-native

L'ancien react-native-testing-library Le nom de package npm existe toujours comme artefact historique, mais les projets actuels devraient utiliser le package scopé maintenu au sein de la famille Testing Library. Le projet dépôt GitHub le décrit comme des utilitaires React Native pour encourager de bonnes pratiques de test, et son historique de releases montre que la bibliothèque a continué d'évoluer avec React Native plutôt que de rester un simple helper statique.

Installez la bibliothèque avec votre environnement Jest existant. Les projets Expo utilisent couramment le preset Jest d'Expo, tandis que les projets React Native « bare » peuvent utiliser le preset React Native :

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

Pour Expo, ajoutez le preset que votre projet utilise déjà :

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

Pour une application React Native bare, utilisez plutôt la configuration Jest React Native correspondante. Gardez le preset aligné sur la version du framework. Beaucoup d'échecs qui ressemblent à des problèmes RNTL viennent d'un renderer React, d'une transformation Babel ou d'un preset Jest incohérent.

Gardez vos fichiers de configuration explicites

Placez la configuration d'environnement partagée dans un fichier de setup plutôt que de répéter les mocks dans chaque test :

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

Référencez-l’ensuite depuis Jest :

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

Si votre application utilise React Navigation, Reanimated, la gestion des gestes, le contexte safe-area ou des modules de stockage, configurez uniquement les mocks dont votre environnement de test a besoin. Un mock global qui modifie le comportement de l'application peut rendre chaque test plus facile à faire passer, tout en rendant la suite de tests moins fiable.

TypeScript nécessite la même attention. Assurez-vous que Jest transforme les .ts et .tsx fichiers via le preset ou votre configuration Babel, et gardez les types de tests disponibles pour le compilateur. Un test qui s'exécute en local mais n'est pas type-checké peut masquer des noms de requêtes incorrects, des paramètres de navigation invalides ou des structures de mocks non sûres.

La chronologie des releases est utile pour diagnostiquer les anciens conseils de configuration. Le projet liste 127 releases, avec v14.0.1 taguée le 2026-06-23, tandis que La v12.9.0, publiée le 27/11/2024, a ajouté le support officiel de React Native 0.77 et d'Expo 52. La branche alpha v14 de mars 2025 est passée à Universal Test Renderer, en remplacement de React Test Renderer déprécié, et s'est préparée à un support exclusif de React 19. Ces détails figurent dans le Historique des releases RNTL, ne copiez donc pas une dépendance de renderer d'un tutoriel obsolète sans vérifier les versions de votre application.

Pour une base Jest concrète, comparez votre configuration à celle-ci les tests unitaires en JavaScript, puis exécutez un petit test de composant avant d'ajouter la navigation et les mocks natifs.

Une infographie montrant le parcours d'installation en cinq étapes pour configurer le projet React Native Testing Library.

API de base, requêtes et assertions expliquées

Les tests RNTL deviennent fiables lorsque leurs requêtes correspondent à ce que l'utilisateur peut voir, trouver et manipuler. L'API est de taille réduite, mais choisir un sélecteur qui expose des détails d'implémentation peut rendre trompeur un test qui passe.

Commencez par render:

const screen = render(<LoginForm />);

Choisissez la requête la plus pertinente pour l’utilisateur. Privilégiez les requêtes axées sur l’accessibilité lorsque le composant les expose, utilisez le texte visible lorsque le texte est le comportement, et réservez testID aux cas sans sélecteur pertinent côté utilisateur ou lorsqu’un hook d’intégration stable est nécessaire.

Un graphique en pyramide illustrant l’ordre de priorité recommandé pour les requêtes dans l’automatisation des tests logiciels.

Choisissez la requête selon le timing et l'intention

Chaque famille de requêtes a un objectif distinct :

  • Présence synchrone : Utiliser getByRole, getByText, ou une autre getBy requête lorsque l’élément doit déjà exister. Le test échoue immédiatement s’il n’existe pas.
  • Apparition asynchrone : Utiliser findByRole ou findByText lorsque le rendu ou une interaction déclenche une mise à jour asynchrone.
  • Vérifications d'absence : Utiliser queryByText ou queryByTestId lorsqu'un élément peut être manquant et qu'un résultat nul est attendu plutôt qu'une exception.
  • Sélecteurs de secours : Utiliser getByTestId volontairement. Il donne aux contrôles complexes un point d'ancrage durable, mais ne doit pas remplacer les libellés accessibles dans toute l'application.

Pour les tests d'événements ciblés, fireEvent.press est direct :

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

Utiliser userEvent lorsque la version installée prend en charge la séquence d'interaction plus réaliste. Dans les deux cas, vérifiez l'interface résultante :

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

Une assertion sur un handler convient à un composant dont le contrat public est un callback d'événement. Elle est faible comme seule preuve que le parcours utilisateur fonctionne.

Privilégiez les assertions spécifiques aux snapshots

De bonnes assertions décrivent l'écran :

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

Ils peuvent également vérifier l'état d'accessibilité, la sélection et le retour de validation visible. Évitez de tester la logique interne :

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

Cette assertion prouve qu'une prop existe, pas que la fonctionnalité marche. De petits snapshots intentionnels peuvent détecter des changements structurels, tandis que les gros snapshots de navigation ou d'écran génèrent souvent des revues bruyantes et facilitent l'approbation de mises à jour inexpliquées.

Le package Testing Library appartient à l'organisation testing-library npm au sens large. Ses packages actifs incluent @testing-library/react-native, avec la version 13.3.3 publiée en 2026, selon les informations du repository du projet. Les conventions de requête partagées sont utiles d'une plateforme à l'autre, mais elles ne déterminent pas quelle assertion représente le comportement de votre produit.

Pour une comparaison plus large des pratiques de test de composants avec Jest, consultez ce guide sur le test unitaire de React. RNTL s’arrête toujours à la frontière des composants rendus en JavaScript. Les permissions natives, les piles de navigation réelles, les claviers d’appareil, le timing des frames et le comportement mémoire nécessitent des outils E2E ou de performance, plutôt que davantage de mocks de composants.

La vidéo ci-dessous illustre le flux de requêtes et d’assertions dans un contexte réel.

Patterns pratiques pour les composants, hooks et la navigation

Une suite de tests utile suit la structure de l’application. Les composants de présentation nécessitent des tests de comportement directs, les hooks des entrées et sorties contrôlées, la navigation un provider suffisamment réaliste, et les modules natifs des mocks qui restent visiblement distincts de la vérification sur appareil réel.

Un ordinateur portable moderne sur un bureau en bois affichant React Native code et un mockup d’application mobile.

Composants de présentation

Gardez un test de composant proche de son contrat public :

const onSelect = jest.fn();

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

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

expect(onSelect).toHaveBeenCalled();

L’étiquette exacte doit correspondre à votre interface. L’essentiel est que le test trouve le contrôle comme le ferait un utilisateur ou un service d’accessibilité, et confirme le résultat visible ou le callback pertinent. Ne mockez pas chaque composant enfant par défaut. Mockez uniquement les frontières coûteuses ou non pertinentes lorsqu’elles obscurcissent le comportement testé.

Pour un hook personnalisé, utilisez renderHook si la version de RNTL installée le fournit :

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

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

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

Le test du hook doit contrôler la frontière réseau ou du dépôt, pas reproduire toute l’application. Testez l’écran séparément pour savoir si l’état du hook devient une interface utile.

Pour le comportement de navigation, rendre un écran dans un vrai NavigationContainer et un petit navigateur de test est souvent plus utile que de mocker chaque méthode de navigation. Appuyez sur un contrôle visible, attendez le contenu de destination et vérifiez la sortie du nouvel écran. Un direct useNavigation mock est toujours pertinent pour un petit bouton dont la seule responsabilité est de déclencher une route typée, mais il ne validera pas l’enregistrement de la route, les paramètres ou le comportement des navigateurs imbriqués.

Les données asynchrones méritent la même rigueur. Mockez la réponse de l’API ou du repository, affichez l’écran, vérifiez l’état de chargement, résolvez la requête, puis vérifiez la sortie de succès ou d’erreur. Utilisez findBy queries pour les éléments attendus à terme et explicitez les requêtes rejetées, sinon un test peut passer parce que le composant n’a jamais atteint la branche prévue.

Mocks de modules natifs

Les mocks pour AsyncStorage, les permissions, les caméras, la biométrie et les API de plateforme sont utiles pour des tests JavaScript déterministes. Ils ne prouvent pas que la fonctionnalité native fonctionne. Gardez le comportement des mocks proche du contrat du module, réinitialisez les appels entre les tests et incluez des réponses d’échec au lieu de modéliser uniquement le cas nominal.

Scénario Idéal avec RNTL Nécessite une validation E2E
Validation de formulaire et erreurs visibles Oui Pas habituellement
UI de chargement, de succès et d’erreur depuis un repository mocké Oui Pour les flux de production critiques
Navigation entre les écrans enregistrés Oui, avec un navigator de test Oui, si les gestes, les deep links ou le comportement de la plateforme importent
Décisions sur l'état avec AsyncStorage Oui, avec un mock contrôlé Oui, si le lancement et la persistance interagissent avec le cycle de vie natif
Caméra, biométrie, permissions ou API de la plateforme Fallback JS et logique de branchement Oui, sur des appareils réels ou représentatifs
Layout, performance de rendu et code natif Non Oui, avec un appareil ou des outils spécialisés

La limite est pratique : mockez la dépendance pour tester vos décisions JavaScript, puis exécutez un test sur appareil pour confirmer le comportement réel de la dépendance.

Débogage de tests instables, CI et contrôles de performance

Un test instable signale souvent un temps non contrôlé, un état partagé ou une assertion qui entre en concurrence avec l’interface. Identifiez la condition présente avant d’augmenter les timeouts. Un timeout plus long peut masquer le problème d’ordonnancement et ralentir la suite.

Utiliser findBy pour les éléments censés apparaître après une mise à jour. Utilisez waitFor pour les conditions d’état ou les appels mockés. Si les faux timers sont activés, avancez-les au point où l’interaction l’exige et restaurez les timers réels ensuite. Un act warning signifie que React a détecté une mise à jour en dehors de sa frontière d’interaction attendue. Corrigez le await, ou le flush du timer, au lieu de masquer l’avertissement.

Rendre les échecs de CI reproductibles

Un job CI fiable installe depuis le lockfile, exécute la même commande Jest qu’en local et isole l’état des mocks. Effacez les appels de mock entre les tests, réinitialisez les modules lorsque l’état au niveau du module influe sur le comportement, et supprimez les dépendances à l’ordre d’exécution. Le cache Jest accélère le feedback, mais tout changement de dépendance ou de configuration nécessite une invalidation du cache appropriée.

Les échecs qui ne se produisent qu’en CI nécessitent une comparaison d’environnement avant de réécrire un composant. Vérifiez Node, le gestionnaire de paquets, les paramètres des workers Jest, la configuration des timers et les variables d’environnement. Reproduisez la même commande en local si possible, puis réduisez le test défaillant à l’interaction minimale qui révèle la différence.

L’observabilité comble un autre manque. Surveillance de la santé de l’app fournit un contexte d’erreurs de production que les tests de composants mockés ne peuvent pas reproduire, notamment les échecs liés aux appareils réels et aux intégrations natives.

Les parcours sensibles à la sécurité nécessitent une seconde frontière. Utilisez les tests de composants pour la validation et les transitions d’état, puis ajoutez une couverture au niveau de l’appareil pour le handoff et le comportement natif. Pour les flux one-time-code, consultez les recommandations pour tester les flux de vérification SMS lorsque cette intégration fait partie du parcours.

Traiter la performance comme une mesure, pas une assertion

Un test fonctionnel peut confirmer qu'une liste s'affiche. Il ne permet pas de déterminer de manière fiable si un refactoring a modifié la durée ou le nombre de rendus, car le temps d'exécution du test ajoute du bruit. Reassure mesure ces valeurs pour un scénario donné, répète le scénario pour réduire la variance et applique une analyse statistique avant de signaler un changement significatif. Sa documentation sur les tests de performance couvre également les rapports adaptés à la CI et à la revue de pull requests.

Évitez les assertions fixes comme « ce rendu doit se terminer sous un seuil donné ». Un worker CI surchargé peut provoquer un faux échec, tandis qu'un seuil trop permissif peut laisser passer une vraie régression. Utilisez des mesures répétées pour détecter les signaux de régression, puis inspectez le composant et le profil de l'appareil lorsque la comparaison montre une différence significative.

Règle de mesure : Les tests fonctionnels vérifient si le comportement est correct. Les outils de performance vérifient si un scénario mesuré a changé. Gardez ces deux questions distinctes.

Récapitulatif et prochaines étapes

React Native Testing Library doit faire partie de la couche étendue et rapide de votre stratégie de test. Elle doit couvrir le comportement des composants, les changements d'état visibles, les résultats d'accessibilité, la validation, les états de données mockées et l'intégration entre les composants JavaScript. Concentrez ces tests sur ce qu'un utilisateur peut observer, et faites en sorte que les échecs pointent vers un comportement spécifique plutôt que vers un arbre de rendu volumineux.

La couche plus restreinte basée sur l'appareil doit protéger les flux où les mocks peuvent être trompeurs. L’ aperçu des tests précise que RNTL ne fournit pas un runtime React Native complet et ne peut pas tester les fonctionnalités natives. Cette même recommandation conseille de coupler les tests de composants avec des outils E2E comme Detox pour les flux critiques, notamment l'authentification, les paiements et les fonctionnalités clés de l'app.

Un parcours de migration pratique

Vous n'avez pas besoin de réécrire une suite existante d'un coup.

  1. Conservez les tests métier utiles. Déplacez l'état pur et la logique métier dans des tests unitaires ciblés pour obtenir des retours clairs.
  2. Remplacez d’abord les assertions sur l’implémentation. Transformez les vérifications des props et de l’état interne en sorties visibles, état d’accessibilité et résultats d’interaction.
  3. Réduisez les snapshots. Conservez uniquement les snapshots que les relecteurs peuvent comprendre et maintenir.
  4. Ajoutez une couverture des frontières natives. Pour chaque module mocké important, identifiez le comportement de l’appareil qui doit encore être validé.
  5. Protégez les parcours critiques. Ajoutez une couverture E2E pour l’authentification, le paiement, la navigation principale, les autorisations et les autres flux où le comportement natif peut changer le résultat.
  6. Mesurez les écrans sensibles séparément. Utilisez des comparaisons de performance répétées pour les listes, les feeds et les chemins de rendu coûteux, plutôt que de deviner d’après les durées Jest.

Pour fiabiliser vos releases, connectez la suite à tests d'intégration CI/CD et exige une couche de test appropriée avant le déploiement. Si un correctif JavaScript ou d'asset doit atteindre les utilisateurs après validation, Capgo peut envoyer des bundles web signés vers des canaux ciblés pour les applications CapacitorJS et Electron, avec des contrôles de déploiement progressif et une protection contre le rollback. Ce workflow de livraison ne remplace pas les tests sur appareil React Native, mais illustre le même principe : valider le comportement à la couche où il s'exécute.

La bonne question n'est pas de savoir si React Native Testing Library peut tester toute votre application. Elle ne le peut pas, et cette limite officielle est utile. La bonne question est de savoir si chaque risque important a un test qui s'exécute dans l'environnement capable de le révéler.


Capgo connecte les changements validés de JavaScript et d'assets à un déploiement contrôlé pour les équipes CapacitorJS et Electron, avec des canaux ciblés, une visibilité sur le rollout et une protection contre le rollback. Visitez Capgo pour voir comment il s'intègre à votre workflow de tests de composants, E2E et CI.

Live updates pour les apps Capacitor

Lorsqu'un bug de la couche web est en ligne, déployez le correctif via Capgo au lieu d'attendre des jours l'approbation des stores. Les utilisateurs reçoivent la mise à jour en arrière-plan, tandis que les changements natifs suivent le circuit de validation habituel.

Les derniers articles du blog

Capgo vous offre les meilleures informations pour créer une application mobile vraiment professionnelle.