Passer au contenu principal

Test unitaire React : Guide pratique de bout en bout

Apprenez à tester unitairement React, de la configuration à CI/CD. Ce guide couvre Jest, RTL, hooks, async code, simulation, et meilleures pratiques pour des applications cross-plateformes robustes.

Martin Donadieu

Martin Donadieu

Spécialiste du contenu

Test unitaire React : Guide pratique de bout en bout

Vous faites une petite modification de l'interface utilisateur avant le déjeuner. Elle semble sans danger. L'étiquette d'un bouton change, une condition de rendu se simplifie, et un hook d'aide prend une nouvelle branch. La demande de tirage est propre, la revue est rapide, et le déploiement est effectué.

Une heure plus tard, le support signale que la connexion s'est arrêtée sur une plateforme. Le Web semble normal. La coquille du bureau de bureau a un chemin de rendu périmé. La construction mobile se comporte différemment après une modification d'état asynchrone. Personne n'a remarqué cela car les code avaient des tests, mais pas les bons tests, et certainement pas un système fiable autour de ces tests.

That’s the main problem with unit testing React in production teams. Writing a few passing tests isn’t hard. Building a suite that still protects you during refactors, release trains, hotfixes, and cross-platform packaging is the hard part. React apps don’t fail because a team forgot how to call render() Ils échouent parce que les tests se dirigent vers les détails d'implémentation, le comportement asynchrone est dissimulé et le CI traite le test comme une case à cocher au lieu d'une porte de lancement.

Le test unitaire moderne de React fonctionne quand il se comporte comme un système de sécurité. Feedback rapide localement. Vérifications déterministes dans le CI. Les limites claires autour de ce qui appartient à un test unitaire et ce qui ne l'appartient pas. Cela compte encore plus quand le même code React est envoyé à travers les navigateurs, les Capacitor conteneurs ou les coques Electron.

Table des matières

Pourquoi le test unitaire de React est votre meilleure protection

Les tests unitaires gagnent leur pain quand ils détectent l'erreur que vous étiez confiant qu'elle ne pouvait pas se produire. Dans React, cela signifie généralement que le composant continue de s'afficher, mais le comportement dont les utilisateurs ont besoin a changé. Un bouton désactivé devient cliquable. Un état de chargement ne se débloque jamais. Un message de remplacement disparaît après une refacteur. Ces erreurs sont petites en code et coûteuses en production.

Le test React a changé de manière importante quand La bibliothèque de test React est devenue le modèle principal pour tester le comportement au lieu des détails internes, poussant les équipes vers des tests qui reflètent le comportement de l'utilisateur plutôt que les propriétés ou l'état des composants, comme le montre la documentation de test de React Native à Vue d'ensemble des tests de React Native. Cette évolution compte parce que les composants React code sont constamment réorganisés. Les hooks se déplacent. Les composants se séparent. Le contexte est introduit. Un test lié à la structure interne se brise pendant des refacteurs sains. Un test lié au comportement visible survit généralement.

Quel est le but d'un test unitaire ?

Un bon test unitaire React protège une petite convention :

  • Sortie de rendu : Le utilisateur voit-il le bon texte, label, état ou fallback ?
  • Comportement d'interaction : Clique, saisie ou basculement changent-ils la UI correctement ?
  • Gestion des limites : Le composant se comporte-t-il correctement lorsqu'il reçoit les entrées attendues, des données manquantes ou une voie d'erreur ?

Un test faible protège la mauvaise chose :

  • Intégrales du composant : Forme de l'état, méthodes privées, props d'implémentation uniquement
  • Mécaniques du framework : Quelle est la manière exacte dont React a mis à jour un crochet en interne
  • Détails de l'enfant : La mise en forme appartenant aux composants imbriqués que vous n'avez pas l'intention de vérifier ici

Règle pratique : Si vous pouvez refacturer le composant sans changer ce que voit ou fait l'utilisateur, le test ne devrait pas non plus avoir besoin de changer.

Les tests unitaires se situent également dans un système de test plus large. Ils ne visent pas à prouver que l'application fonctionne de bout en bout. Ils constituent la couche rapide qui attrape les régressions avant que vous n'ayez besoin d'un test au niveau du navigateur ou d'une validation au niveau du dispositif. C'est pourquoi ils constituent la première ligne de défense dans n'importe quel empilement sensé de tests automatisés pour les applications de production Pour les équipes React qui acheminent souvent, la confiance provient de cette division du travail. Les tests unitaires attrapent rapidement les régressions locales. Les tests d'intégration vérifient les joints. Les tests fin-à-fin confirment les chemins critiques. Omettre la couche unitaire et tout ce qui est plus lent en aval doit supporter trop de charge.

Configuration de votre environnement de test React moderne

Un environnement de test fragile crée des tests flous avant même d'avoir écrit une seule assertion. Beaucoup de développeurs blâment Jest, jsdom ou React lorsque le problème sous-jacent est une configuration incohérente à travers les machines locales et CI. La solution est de rendre l'environnement banal. Banal est bon ici

Un espace de travail propre avec un moniteur d'ordinateur affichant les tests unitaires React __CAPGO_KEEP_0__ dans un __CAPGO_KEEP_1__ éditeur

A clean workspace featuring a computer monitor displaying React unit testing code in a code editor.

__CAPGO_KEEP_0__

Pour une application React moderne, en particulier celle créée avec Vite, la configuration de base devrait inclure :

  • Un exécuteur de tests : Jest reste courant, surtout dans les anciens codebases React et les stacks CI d'entreprise.
  • Un environnement simulant un navigateur : jsdom pour que les tests de composants rendent la sortie DOM.
  • Utilitaires de Testing Library : @testing-library/react et @testing-library/jest-dom
  • Un seul point d'entrée de configuration : Un seul fichier pour enregistrer les matcheurs et les mocks globaux.

Le workflow clé que la guidance de test de React renforce est simple : rendre le composant dans un environnement jsdom, interroger l'interface utilisateur avec des sélecteurs comme getByText ou getByRole, déclencher l'interaction et affirmer le changement de la sortie DOM, comme décrit dans le Documentation de test ReactCet environnement de test reste fiable uniquement si chaque machine exécute le même environnement de test.

Un setup Jest pratique ressemble généralement à ceci :

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapper: {
    '\\.(css|less|scss)$': 'identity-obj-proxy',
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
  },
};

Si votre équipe utilise SWC au lieu de Babel, cela ne pose pas de problème. Le point n'est pas le transformateur. Le point est la cohérence. Choisissez une voie et standardisez-la dans le dépôt. Si vous souhaitez une référence complémentaire pour les conventions de test JavaScript plus larges, le guide des tests unitaires de Capgo est un document de transfert d'équipe utile. Ajoutez le fichier de configuration que votre ensemble de tests dépendra

Un setup approprié

élimine beaucoup de bruit répétitif : setupTests.js Ce fichier est où vous résolvez les écarts d'environnement une fois au lieu de les résoudre à l'intérieur de vingt fichiers de test. Ajoutez des mocks pour les API que votre bibliothèque UI dépend, telles que

import '@testing-library/jest-dom';

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

ou matchMedia, ResizeObserversi votre bibliothèque de composants s'y attend. IntersectionObserverunit tests in JavaScript guide

Sans cela, les développeurs patchent les globaux ad hoc. Cela crée des tests incohérents et des échecs difficiles à suivre. La mise en œuvre locale passe car quelqu'un a ajouté un mock manuel dans un fichier. Le CI échoue car le paramétrage n'a pas été partagé.

Maintenez le comportement local et CI alignés

Le commandement local devrait correspondre au plus possible au commandement CI. Si les développeurs exécutent la commande watch avec des paramètres permissifs mais que CI exécute une configuration plus stricte, vous obtiendrez des échecs inattendus après fusion. Gardez les scripts explicites :

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:ci": "jest --runInBand --coverage"
  }
}

Un court parcours aide les nouveaux membres de l'équipe à obtenir rapidement la même base :

La plus grande décision d'implémentation est la discipline autour des paramètres par défaut. Mettez les alias dans la configuration. Mettez les mocks d'environnement dans un fichier de mise en œuvre unique. Utilisez jsdom pour les tests de l'interface utilisateur et un environnement plus léger pour les utilitaires purs lorsque possible. Moins de comportement personnalisé que chaque test individuel nécessite, plus votre système devient fiable.

Écrire des tests de composants significatifs

Les organisations n'ont pas de problème pour écrire des tests. Elles ont un problème pour écrire des tests qui ont encore de l'importance six mois plus tard.

Le modèle standard pour la test unitaire des composants React est toujours le bon : rendez le composant, interrogez l'interface utilisateur avec des sélecteurs centrés sur l'utilisateur, déclenchez une interaction et affirmez la modification du DOM résultante, qui maintient les tests à l'écart des détails d'implémentation comme l'état ou les props, comme décrit dans le guide de test React. La clé est d'appliquer ce modèle avec modération.

Testez l'accordéon comme un utilisateur l'utilise

Prenez un composant de base. Il affiche un bouton avec un titre. Le contenu du panneau commence caché. En cliquant sur le bouton, le contenu est révélé et l'état d'accèsibilité est mis à jour. Accordion Cela suffit pour plusieurs tests utiles :

La première mise en page affiche le titre mais pas le contenu.

  1. En cliquant sur le déclencheur, le contenu est révélé.
  2. En cliquant à nouveau, il se replie.
  3. Les attributs d'accèsibilité reflètent l'état visible.
  4. Ce dernier point est souvent négligé. Si votre composant utilise aria-*, ou une structure basée sur le rôle, vérifiez-les. Ce ne sont pas des détails d'implémentation. C'est une partie du contrat utilisateur.

Les meilleures tests de composants lisent comme un rapport de bogues que vous ne souhaitez jamais recevoir. aria-expanded, aria-controls__CAPGO_KEEP_0__

Initial render shows the title but not the content.

Choisissez les requêtes en fonction de l'intention

La bibliothèque de test React vous offre plusieurs styles de requêtes, mais ils ne sont pas interchangeables. Le choix du mauvais style rend les tests bruyants ou trompeurs.

Type de Requête Lorsque l'élément est trouvé Lorsque l'élément n'est pas trouvé Exemple d'utilisation
getBy Renvoie l'élément immédiatement Lance une erreur immédiatement Assurez-vous que le bouton ou l'en-tête doit déjà être affiché sur l'écran
queryBy Renvoie l'élément immédiatement Renvoie null Assurez-vous que le contenu caché n'existe pas avant l'interaction
findBy S'accomplit lorsque l'élément apparaît Rejette après avoir attendu Affirme que le contenu chargé en parallèle apparaît après une requête ou une mise à jour retardée

Un modèle mental simple aide :

  • Utilisez getBy pour les choses qui doivent déjà exister.
  • Utilisez queryBy pour les choses qui ne doivent pas exister encore.
  • Utilisez findBy lorsque les modifications de l'interface utilisateur ont lieu plus tard.

Si un test commence par findBy pour tout, cela signifie généralement que l'auteur n'est pas sûr de quand le composant se met à jour. Cette incertitude devient ensuite de la flottabilité ultérieurement.

Un exemple d'accordéon pratique

Voici un composant représentatif :

function Accordion({ title, children }) {
  const [open, setOpen] = React.useState(false);

  return (
    <section>
      <button
        aria-expanded={open}
        aria-controls="accordion-panel"
        onClick={() => setOpen(prev => !prev)}
      >
        {title}
      </button>
      {open ? (
        <div id="accordion-panel">
          {children}
        </div>
      ) : null}
    </section>
  );
}

Et voici la forme des tests à conserver :

import { render, screen, fireEvent } from '@testing-library/react';

test('renders the accordion title and hides content initially', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  expect(screen.getByRole('button', { name: /shipping details/i })).toBeInTheDocument();
  expect(screen.queryByText(/delivery takes 3 days/i)).not.toBeInTheDocument();
});

test('reveals content when the trigger is clicked', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  fireEvent.click(screen.getByRole('button', { name: /shipping details/i }));

  expect(screen.getByText(/delivery takes 3 days/i)).toBeInTheDocument();
});

test('updates aria-expanded when opened', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  const button = screen.getByRole('button', { name: /shipping details/i });
  expect(button).toHaveAttribute('aria-expanded', 'false');

  fireEvent.click(button);

  expect(button).toHaveAttribute('aria-expanded', 'true');
});

Ce qui manque est tout aussi important. Il n'y a pas d'assertion contre l'état interne. Pas de vérification de ce qui a été appelé. Pas de snapshot de l'ensemble de l'arbre rendu. Ces tests ajouteraient de la maintenance, pas de confiance. setOpen Un certain nombre de habitudes rendent les tests de composants plus solides :

Préférez les requêtes basées sur le rôle :

  • Les boutons, les titres, les dialogues, les alertes et les champs de saisie devraient généralement être trouvés par rôle. Gardez chaque test étroit :
  • Un comportement visible par l'utilisateur par test garde les échecs lisibles. Nommez les tests après les résultats :
  • “met à jour aria-expanded lors de l'ouverture” est beaucoup plus utile que “fonctionne correctement.” __CAPGO_KEEP_0__

Si un composant est difficile à tester à travers le DOM, cela révèle souvent un problème de conception. Peut-être qu'il cache l'état dans un mauvais endroit. Peut-être qu'il manque de balisage sémantique. Les bonnes tests poussent souvent les équipes vers de meilleurs composants.

Tester les Hooks personnalisés et la logique d'application

Les applications React cachent beaucoup de comportements importants en dehors des composants. Les transitions d'état vivent dans les hooks. La validation et la mise en forme vivent dans les fonctions d'aide. La mise en forme des données se produit souvent avant que quoi que ce soit ne s'affiche. Si vous n'avez que des composants visibles, vous manquerez une grande partie de la code qui peut toujours casser le comportement de production.

Les hooks ont besoin d'un harnais React-aware

Un hook personnalisé a toujours besoin de React pour s'exécuter correctement, donc testez-le avec renderHook et enveloppez les appels modifiant l'état dans act().

Un petit useToggle hook est un bon exemple :

import { useState, useCallback } from 'react';

export function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = useCallback(() => setValue(current => !current), []);
  return { value, toggle };
}

Son test doit rester axé sur le contrat public :

import { renderHook, act } from '@testing-library/react';
import { useToggle } from './useToggle';

test('returns the initial value', () => {
  const { result } = renderHook(() => useToggle(true));
  expect(result.current.value).toBe(true);
});

test('toggles the value', () => {
  const { result } = renderHook(() => useToggle(false));

  act(() => {
    result.current.toggle();
  });

  expect(result.current.value).toBe(true);
});

Cette mise à l'épreuve est utile car le hook lui-même est l'unité. Vous n'êtes pas en train de tester les internes de React. Vous vérifiez le comportement externe du hook.

Pour les équipes de produits créant des UI ou des primitives de fonctionnalités réutilisables, ce modèle compte beaucoup. Les hooks deviennent souvent l'interface partagée entre les applications, les systèmes de conception ou les outils internes. Si vous concevez un comportement réutilisable avec une intention commerciale, consultez les ressources sur les hooks pour les produits de créateurs peut aider à considérer les appels de fonctions comme des blocs de construction standardisés plutôt que comme des détails d'implémentation.

La logique pure doit rester pure dans les tests

Tout n'a pas besoin de jsdomReact, ou Testing Library. Si une fonction est pure, testez-la avec Jest simple dans un environnement Node.

Exemple :

export function formatDisplayName(firstName: string, lastName: string) {
  return `${firstName.trim()} ${lastName.trim()}`.trim();
}

Cette fonctionnalité devrait être simple comme la mort :

import { formatDisplayName } from './formatDisplayName';

test('joins and trims both names', () => {
  expect(formatDisplayName(' Ada ', ' Lovelace ')).toBe('Ada Lovelace');
});

test('handles a missing last name', () => {
  expect(formatDisplayName('Ada', '')).toBe('Ada');
});

La victoire ici est la vitesse et la clarté. Lorsqu'une fonction n'a pas besoin d'un arbre de rendu, ne lui en donne pas. Les outils spécifiques à React ajoutent un surcoût. Gardez les tests de logique commerciale petits, rapides et proches de la fonction qu'ils vérifient.

Un découpage pratique fonctionne bien :

  • Appels de fonctions : Utilisez renderHook, act(), et les fournisseurs de wrapper lorsque nécessaire.
  • Utilités : Utilisez Jest simple et sans DOM.
  • Logique transversale étatique : Insérez-le dans des aides de testables lorsque le test du composant commence à faire trop de travail.

Les équipes ont souvent surchargé les tests de composants de logique avec des assertions qui appartiennent à une couche plus basse. En extrayant cette logique, vous obtenez deux avantages. Le test du composant devient plus propre, et le test de logique devient plus rapide.

Maîtriser les techniques avancées : Mocking et Async

La plupart des suites de tests React peu fiables se cassent en deux endroits. Ils se cassent aux limites de dépendance, et ils se cassent autour du temps.

C'est pourquoi le test asynchrone et le mockage sont la ligne de démarcation entre une suite de tests de jouet et une que vous pouvez vous fier avant la mise en production. Une analyse attribue 46,5% de la flottabilité des tests aux problèmes liés à l'environnement ou aux ressources, comme les temps d'attente asynchrones dans ce rapport d'analyse de tests React. Dans les applications React, cela se traduit directement par les transitions d'état, le rendu retardé, l'interface utilisateur dérivée du réseau et les tests qui supposent au lieu de attendre de manière déterministe.

Un tableau de comparaison montrant les techniques de test avancées de React, en particulier en mettant l'accent sur le mockage des dépendances par rapport au test asynchrone.

Mimique les limites, pas chaque couche

La façon la plus rapide d'écrire un test trompeur est de simuler la moitié de votre arbre de composant et puis d'affirmer que vos propres simulations ont fonctionné.

Pour un composant qui récupère les données de compte, simulez le client de réseau ou le module API. N'ayez pas peur de simuler l'appel à l'API, les hooks, les composants enfants, les indicateurs de chargement et les trois fonctions d'utilité, à moins que le test nécessite vraiment une isolation à ces points de jonction.

Utilisez ce jeu de règles :

  • Simulez les services externes : Les clients HTTP, les analyses, les API du navigateur uniquement, les ponts natifs
  • Simulez les API de plateforme instables : matchMediaLes temporisateurs, les interfaces de préchargement Electron, les plugins Capacitor lorsqu'ils ne sont pas disponibles dans jsdom
  • Évitez de simuler vos propres internes par défaut : Les hooks personnalisés, les enfants simples, les utilitaires locaux

Si un test passe parce que toutes les parties difficiles ont été remplacées par des faux, il n'a pas acheté beaucoup de confiance dans la mise en production.

Pour les équipes qui veulent des exemples et des modèles autour des API de l'exécuteur, utilisez Capgo Catégorie Jest Il s'agit d'une bibliothèque de référence pratique, notamment lors de l'incorporation de développeurs qui connaissent React mais pas les mécanismes de test.

Les tests asynchrones échouent lorsque le temps est vague

Les échecs asynchrones proviennent généralement d'une des trois erreurs :

  1. Le test affirme trop tôt.
  2. Le test attend avec des temporisations arbitraires.
  3. Le composant se met à jour plus d'une fois, mais le test ne modélise qu'une seule transition.

Un test asynchrone stable a généralement cette forme :

test('shows user details after data loads', async () => {
  render(<UserProfile userId="42" />);
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  expect(await screen.findByText(/account owner/i)).toBeInTheDocument();
});

Ou, lorsque vous avez besoin d'attendre une condition spécifique :

await waitFor(() => {
  expect(screen.getByRole('alert')).toBeInTheDocument();
});

Utilisez findBy lorsque l'apparition d'un élément est l'événement dont vous vous souciez. Utilisez waitFor lorsque la condition est plus large ou que l'état ne peut pas être exprimé avec une seule requête. Évitez setTimeout à moins que vous ne testiez explicitement le comportement du chronomètre et que vous utilisiez des temporisations de fake.

L'écosystème de test de React attend également que vous respectiez act() les sémiotiques autour des mises à jour. Testing Library gère une grande partie de cela pour vous, mais si vous avancez manuellement l'état ou les temporisations, vous devez encore réfléchir à quand les mises à jour se vidangent.

Savoir quelle outil de simulation utiliser

Les outils de simulation différents résolvent différents problèmes :

Outil Meilleur usage Erreur commune
jest.fn() Appels de rappel ou fonctions injectées seuls L'utiliser pour remplacer un module entier lorsqu'un simple appel de rappel suffit
jest.spyOn() Observer ou surcharger une méthode sur un objet ou module réel Se souvenir de restaurer l'implémentation d'origine
jest.mock() Remplacez une dépendance de module à la limite de l'importation La mise en miroir de modules importants par défaut et la perte de comportement significatif

Les exemples vous aident :

  • Rappelez-vous jest.fn() lorsqu'un composant prend un onSubmit attribut.
  • Utilisez jest.spyOn() lorsque vous avez besoin de vérifier console.error, une méthode de stockage ou une fonction exportée API.
  • Utilisez jest.mock() lorsque l'importation d'un module aurait autrement heurté I/O, native code, ou le comportement en dehors de la limite de l'unité.

Un domaine avancé que de nombreux guides négligent est la testabilité de la voie d'erreur dans React moderne. Les limites d'erreur, les changements de l'état retardés et les UI de fallback asynchrones méritent des tests de premier ordre, pas seulement l'exemple de clic « heureux ». Si un enfant lance une erreur, affirmez l'UI de fallback. Si une requête échoue, affirmez l'état de récupération visible. Si un bouton est désactivé pendant le chargement, affirmez aussi cela. Ce sont les bogues que les utilisateurs se rappellent.

Améliorer la qualité et la stratégie des tests

Beaucoup d'équipes poursuivent toujours la couverture comme si c'était la même chose que la confiance. Ce n'est pas le cas.

Vous pouvez atteindre un objectif de couverture et manquer encore les régressions qui comptent. Un ensemble complet d'assertions superficielles, de captures d'écran larges et d'internes mockés créent l'apparence de sécurité tout en augmentant les coûts de maintenance.

Un infographic comparant les avantages de la qualité de test par rapport à l'overhead de maintenance des suites de tests à haute quantité.

La couverture est une carte, pas le but

Les rapports de couverture sont utiles lorsqu'ils répondent à une seule question : quels chemins critiques n'ont pas encore de protection ?

Ils ne sont pas utiles lorsqu'ils poussent les développeurs à tester des enveloppes triviales, du markup statique ou des fichiers de passage d'une ligne juste pour déplacer un pourcentage. Traitez la couverture comme un outil de découverte. Si l'état d'authentification, les actions de facturation, les drapeaux de fonctionnalité ou les invitations à mettre à jour n'ont pas de tests, c'est un signal. Si un composant d'icône présentationnel n'a pas de tests, ce n'est généralement pas le cas.

Une question de revue saine est simple : cette test réduit-t-il le risque de lancement ?

  • Oui : Il vérifie le comportement visible de l'utilisateur sur un chemin critique.
  • Peut-être : Il protège la logique métier qui est facile à briser pendant la refacturation.
  • Non: Il affirme les détails d'implémentation ou duplique la valeur d'un autre test.

Ce que vous ne devez pas tester unitairement

Beaucoup de guides React ne passent pas suffisamment de temps sur l'omission. Cette lacune compte car le sur-mockage et le test des détails d'implémentation créent des ensembles fragiles qui passent alors que l'expérience utilisateur se brise, comme le note le guide de BrowserStack sur ce que vous ne devez pas tester unitairement dans React.

Omettez ou limitez fortement ces modèles :

  • Les affirmations d'état interne : N'essayez pas de tester isOpen directement lorsque vous pouvez tester si le panneau s'est ouvert.
  • Le comportement du framework : N'essayez pas de tester que React a appelé un effet. Testez le résultat de ce que l'effet change.
  • Les internes de bibliothèque tierce : Testez votre intégration avec un sélecteur de date ou un routeur, pas la logique de rendu de la bibliothèque elle-même.
  • Unités surchargées : Si vous avez mocké tous les enfants et les helpers, vous ne testez peut-être plus un comportement significatif.

Les mauvaises tests sont pires que les tests manquants lorsqu'ils bloquent les réfacteurs et ne parviennent pas à détecter les bogues de production.

Un bon heuristique est la propriété des limites. Testez ce que votre code possède. N'essayez pas de tester ce que React, le navigateur ou une bibliothèque mature possèdent, à moins que votre couche d'intégration change le contrat.

Où les captures d'écran sont utiles et où elles nuisent

Les captures d'écran ne sont pas inutiles. Elles sont juste faciles à mal utiliser.

Utilisez-les avec parcimonie pour les composants avec une sortie stable et simple où une large différence structurale est significative. Évitez-les pour les composants interactifs ou dynamiques car ils deviennent du bruit. Les développeurs cessent de les lire et commencent à les mettre à jour de manière réflexe.

Des alternatives meilleures existent généralement :

  • Pour la rendu conditionnel, assurez-vous de la présence ou de l'absence du texte clé.
  • Pour les changements d'état visuels, assurez-vous du rôle, de l'étiquette ou de l'attribut qui compte.
  • Pour les erreurs et les fallbacks, assurez-vous du message ou de la région d'alerte réel.

Si votre équipe a besoin d'un processus qualité plus large que les tests unitaires, un partenaire solide est un flux de garantie de qualité d'application qui traite les tests, les contrôles de mise en production et la planification de reversion comme un système unique. C'est le changement d'attitude qui améliore la qualité des tests le plus rapidement. Arrêtez de demander combien de tests vous avez. Commencez à demander quelles erreurs pourraient encore atteindre les utilisateurs. Intégration des Tests dans une Chaîne de Production Continu (CI/CD) Multiformat Un ensemble de tests qui ne s'exécute que sur un ordinateur de développement est une suggestion, pas un contrôle.

L'ensemble devient opérationnel lorsque chaque demande de tirage exécute les mêmes contrôles dans un environnement propre et bloque les mises en merge lorsque ces contrôles échouent. Cela semble évident, mais beaucoup d'équipes laissent encore des lacunes critiques. Les tests sont exécutés manuellement. Les rapports de couverture sont optionnels. Les tâches de packaging et de mise en production commencent avant que les tâches de test aient terminé. C'est ainsi que les petites régressions d'interface glissent dans les plus grandes erreurs de mise en production.

Un diagramme de flux à cinq étapes illustrant le processus d'intégration des tests automatisés React dans une chaîne de développement CI/CD.

Une demande de tirage devrait déclencher le même contrôle chaque fois

Pour tester React comme un filet de sécurité, la CI a besoin de quelques éléments essentiels :

Exécuter sur chaque demande de tirage

Installer les dépendances à partir du fichier de verrouillage

  • Utiliser la même commande de test chaque fois
  • For unit testing React to act like a safety net, CI needs a few essentials:__CAPGO_KEEP_0__
  • A five-step flowchart illustrating the process of integrating React automated tests into a CI/CD development pipeline.__CAPGO_KEEP_0__
  • Faillez rapidement en cas de failures de test
  • Publiez les artefacts uniquement après que les tests passent

C'est le cœur des pratiques de déploiement continu pour les équipes d'applications. Construisez la confiance avant la mise en production, et non après.

A simple GitHub Actions workflow is enough for many teams:

name: test

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  react-tests:
    runs-on: ubuntu-latest

    steps:
      - name: Check out code
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm run test:ci

Cela n'est pas élégant, et c'est justement le point. Les pipelines les plus solides sont souvent les moins surprenants.

Pourquoi cela compte plus pour Capacitor et Electron

Les applications React cross-plateformes comportent plus de risque de mise en production que les applications browser-only car la même interface code est souvent déployée dans différents conteneurs avec différentes hypothèses de runtime.

Un quelques exemples montrent où les pipelines sont utiles :

  • Capacitor applications : Les applications web code peuvent passer localement mais échouer lorsqu'une passerelle de plugin, un état hors ligne ou un cas d'échappement de cycle d'application change le comportement après le packaging.
  • Applications Electron : Un composant de rendu peut dépendre d'APIs de préchargement, de messages de fenêtre ou d'état de bureau uniquement qui n'existeront pas dans des tests de navigateur classiques à moins d'être simulés intentionnellement.
  • Trains de publication partagés : Une mauvaise archive peut avoir un impact sur plusieurs cibles si votre processus de déploiement n'entoure pas étroitement la publication.

C'est pourquoi les tests unitaires doivent s'exécuter avant les tâches de packaging, et les tâches de packaging doivent s'exécuter avant les tâches de distribution. Chaque étape réduit le risque. Les tests unitaires détectent rapidement les régressions locales. La vérification de l'environnement du packaging vérifie les hypothèses de l'environnement. L'approbation manuelle ou le lancement étalé gère la confiance finale de la mise en production.

Un workflow d'Actions pratique GitHub :

Un pipeline plus mature divise généralement les responsabilités :

  1. Tâche de test : Tests unitaires rapides et de crochet
  2. Tâche de construction : Mise en production uniquement après que les tests passent
  3. Tâche de packaging : Capacitor synchronisation, packaging d'Electron ou regroupement d'artefacts
  4. Lancement de job : Publiez uniquement à partir de branches ou de tags approuvés

Pour les équipes qui délivrent des mises à jour en direct à Capacitor ou Electron, c'est là que les outils de lancement sont importants. Une option dans ce flux de travail est Capgo, qui publie des bundles web signés pour les applications CapacitorJS et Electron avec un support de retrait et des contrôles de lancement basés sur les canaux. En pratique, cela signifie que votre job de test React peut agir comme la première barrière dure avant que tout bundle web ne soit promu vers la livraison de production ou de mise en ligne.

La règle opérationnelle est simple. N'obligez pas l'infrastructure de lancement à compenser les tests faibles. Utilisez l'infrastructure de lancement après que les tests fiables aient déjà éliminé les changements mauvais.

Un système de test fiable change le comportement de l'équipe. Les ingénieurs fusionnent avec moins de hésitation. Les réviseurs se concentrent sur les cas d'extrémité au lieu de réexécuter les bases manuellement. Les gestionnaires de lancement ne traitent plus chaque déploiement comme un jeu d'hasard. C'est le résultat de faire des tests unitaires React bien.


Si votre équipe délivre React à travers Capacitor ou Electron, la sécurité de lancement dépend de plus que des tests locaux verts. Capgo fournit aux équipes un moyen contrôlé de publier des mises à jour web signées, de cibler les canaux de lancement et de reculer les bundles mauvais sans attendre la revue de la boutique, ce qui convient naturellement derrière un pipeline CI qui exige déjà que les tests unitaires passent avant la déployment.

Mises à jour en temps réel pour les applications Capacitor

Lorsqu'une bug de couche web est en direct, expédiez la correction par Capgo au lieu d'attendre des jours pour l'approbation de la boutique d'applications. Les utilisateurs reçoivent la mise à jour en arrière-plan tandis que les changements natifs restent dans le chemin de revue normal.

Commencez maintenant

Dernières actualités de notre blog

Capgo vous donne les meilleures informations dont vous avez besoin pour créer une application mobile véritablement professionnelle.