Table des matières
Test de début de réaction
Option 1: tests unitaires
Option 2: tests d'intégration
Alors, qu'est-ce qui est requis pour les tests unitaires?
Autres avantages
Bloc d'attente effacer
Les commentaires en ligne
Étapes suivantes pour l'équipe
Maison interface Web tutoriel CSS Test d'intégration réagi: une plus grande couverture, moins de tests

Test d'intégration réagi: une plus grande couverture, moins de tests

Apr 07, 2025 am 09:20 AM

Test d'intégration réagi: une plus grande couverture, moins de tests

Pour les sites Web interactifs comme ceux construits avec React, les tests d'intégration sont un choix naturel. Ils valident comment les utilisateurs interagissent avec les applications sans les frais généraux supplémentaires de tests de bout en bout.

Cet article l'illustre avec un exercice qui commence par un site Web simple, utilise des tests unitaires et des tests d'intégration pour vérifier les comportements et montre comment les tests d'intégration peuvent atteindre une plus grande valeur avec moins de lignes de code. Cet article suppose que vous connaissez les tests dans React et JavaScript. La familiarité avec la bibliothèque des tests de plaisanterie et de réaction peut être utile, mais pas requise.

Il existe trois types de tests:

  • Les tests unitaires vérifient indépendamment un morceau de code. Ils sont faciles à écrire, mais peuvent ignorer la vue d'ensemble.
  • Les tests de bout en bout (E2E) utilisent un cadre automatisé tel que Cypress ou Selenium pour interagir avec votre site Web comme les utilisateurs: charger des pages, remplir des formulaires, cliquer sur les boutons, etc. Ils sont généralement écrits et s'exécutent plus lentement, mais sont très proches de l'expérience utilisateur réelle.
  • Les tests d'intégration se trouvent quelque part entre les deux. Ils vérifient comment plusieurs unités d'une application fonctionnent ensemble, mais sont plus légères que les tests E2E. Par exemple, la plaisanterie est livrée avec certains services publics intégrés pour faciliter les tests d'intégration; Jest utilise JSDom en arrière-plan pour simuler les API du navigateur commun, avec moins de frais généraux que l'automatisation, et ses puissants outils de moquerie peuvent simuler des appels API externes.

Une autre chose à noter: dans les applications React, les tests unitaires et les tests d'intégration sont écrits de la même manière et les outils sont utilisés .

Test de début de réaction

J'ai créé une application React simple (disponible sur github) avec un formulaire de connexion. Je l'ai connecté à reqres.in, qui est une API pratique que j'ai trouvé à utiliser pour tester les projets frontaux.

Vous pouvez vous connecter avec succès:

... ou rencontrez un message d'erreur de l'API:

La structure du code est la suivante:

 <code>LoginModule/ ├── components/ │ ├── Login.js // 渲染LoginForm、错误消息和登录确认│ └── LoginForm.js // 渲染登录表单字段和按钮├── hooks/ │ └── useLogin.js // 连接到API 并管理状态└── index.js // 将所有内容整合在一起</code>
Copier après la connexion

Option 1: tests unitaires

Si vous aimez écrire des tests comme moi - peut-être porter des écouteurs et jouer de la belle musique sur Spotify - alors vous pourriez être incapable de résister aux tests d'unité d'écriture pour chaque fichier.

Même si vous n'êtes pas un passionné de test, vous pouvez travailler sur un projet qui "essaie de faire un bon travail de test" sans stratégie claire, et la méthode de test est "Je pense que chaque fichier devrait avoir son propre test?"

Cela ressemble à ceci (j'ai ajouté l'unité dans le nom du fichier de test pour plus de clarté):

 <code>LoginModule/ ├── components/ │  ├── Login.js │  ├── Login.unit.test.js │  ├── LoginForm.js │  └── LoginForm.unit.test.js ├── hooks/ │  ├── useLogin.js │  └── useLogin.unit.test.js ├── index.js └── index.unit.test.js</code>
Copier après la connexion

J'ai terminé l'exercice sur GitHub pour ajouter tous ces tests unitaires et créé un test: couverture: script unitaire pour générer des rapports de couverture (une fonction intégrée de la plaisanterie). Nous pouvons obtenir une couverture à 100% via quatre fichiers de test unitaires:

Une couverture à 100% est généralement écrasante, mais il est possible pour une base de code aussi simple.

Croflons-nous dans l'un des tests unitaires créés pour le crochet React Onlogin. Si vous n'êtes pas familier avec React Hooks ou comment les tester, ne vous inquiétez pas.

 TEST («Flow de connexion réussie», async () => {
 // Simuler la plaisanterie de réponse à l'API réussie
  .spyon (fenêtre, 'fetch')
  .mockResolvedValue ({json: () => ({token: '123'})});

 const {result, waitForNextupDate} = renderHook (() => usElogin ());

 acte (() => {
  result.current.onsubmit ({
   e-mail: '[e-mail protégé]',
   Mot de passe: «mot de passe»,
  });
 });

 // définir le statut en attente
 attendre (result.current.state) .toequal ({
  Statut: «en attente»,
  Utilisateur: NULL,
  Erreur: null,
 });

 attendre waitforNextupDate ();

 // Définissez le statut sur résolu et stockez l'adresse e-mail attendre (result.current.state) .toequal ({
  Statut: «résolu»,
  utilisateur: {
   e-mail: '[e-mail protégé]',
  },
  Erreur: null,
 });
});
Copier après la connexion

Ce test est intéressant à écrire (car la bibliothèque de tests React Hooks fait des crochets de test), mais il a quelques problèmes.

Premièrement, l'état interne de validation de test passe de «en attente» à «résolu»; Ces détails d'implémentation ne sont pas exposés à l'utilisateur, donc ce n'est peut-être pas une bonne chose à tester. Si nous refacteurs l'application, nous devrons mettre à jour ce test, même si rien ne change du point de vue de l'utilisateur.

De plus, en tant que test unitaire, ce n'est qu'une partie de celui-ci. Si nous voulons vérifier les autres fonctionnalités du processus de connexion, tels que la modification du texte du bouton de soumission en "chargement", nous devrons le faire dans un fichier de test différent.

Option 2: tests d'intégration

Examinons l'ajout d'une alternative au test d'intégration pour valider ce processus:

 <code>LoginModule/ ├── components/ │  ├── Login.js │  └── LoginForm.js ├── hooks/ │  └── useLogin.js ├── index.js └── index.integration.test.js</code>
Copier après la connexion

J'ai implémenté ce test et un test: Couverture: script d'intégration pour générer des rapports de couverture. Tout comme les tests unitaires, nous pouvons atteindre une couverture à 100%, mais cette fois, tout est dans un seul fichier et nécessite moins de lignes de code.

Voici les tests d'intégration couvrant le processus de connexion réussi:

 TEST ('Connexion réussie', async () => {
  plaisanter
    .spyon (fenêtre, 'fetch')
    .mockResolvedValue ({json: () => ({token: '123'})});

  rendre(<loginmodule></loginmodule> ));

  const e-mail e-mail = screen.getByRole ('textbox', {name: 'email'});
  const MotwordField = screen.getByLabelText («mot de passe»);
  const Button = Screen.getByRole ('Button');

  // Rempliez et soumettez le formulaire fireevent.change (champ de messagerie, {cible: {valeur: '[e-mail protégé]'}});
  FireEvent.Change (Passwordfield, {Target: {Value: 'Password'}});
  FireEvent.Click (bouton);

  // il définit le statut de charge attendre (bouton) .tobedisabled ();
  attendre (bouton) .toHaveTextContent («Chargement ...»);

  attendre waitfor (() => {
    // il cache l'élément de forme attendre (bouton) .not.tobeIntheDocument ();
    Attendez-vous (champ de messagerie) .not.tobeIntheDocument ();
    Attendez-vous (Passwordfield) .not.tobeIntheDocument ();

    // Il affiche le texte de réussite et l'adresse e-mail const loggEdIntext = screen.getByText ('connecté en As');
    attendre (loggedIntext) .TobeIntheDocument ();
    const emailAddressText = screen.getByText ('[e-mail protégé]');
    attendre (emailAddressText) .TobeIntheDocument ();
  });
});
Copier après la connexion

J'aime vraiment ce test car il vérifie l'intégralité du processus de connexion du point de vue d'un utilisateur: formulaires, statut de chargement et messages de confirmation réussis. Les tests d'intégration sont excellents pour les applications React, précisément en raison de ce cas d'utilisation; L'expérience utilisateur est ce que nous voulons tester, ce qui implique presque toujours plusieurs extraits de code différents fonctionnant ensemble .

Ce test ne comprend pas les composants ou les crochets qui font fonctionner le comportement attendu, ce qui est bien. Tant que l'expérience utilisateur reste la même, nous pouvons réécrire et refacter ces détails d'implémentation sans casser le test.

Je ne creuserai pas dans l'état initial du processus de connexion et d'autres tests d'intégration pour la gestion des erreurs, mais je vous encourage à les voir sur GitHub.

Alors, qu'est-ce qui est requis pour les tests unitaires?

Plutôt que de considérer les tests unitaires par rapport aux tests d'intégration, prenons du recul et réfléchissons à la façon dont nous décidons de ce que nous devons tester en premier lieu. LoginModule doit être testé car c'est une entité que nous voulons que les utilisateurs (autres fichiers de l'application) puissent utiliser en toute confiance.

D'un autre côté, il n'est pas nécessaire de tester le crochet Onlogin, car ce ne sont que les détails d'implémentation de LoginModule. Cependant, si nos exigences changent et que Onlogin a des cas d'utilisation ailleurs, nous devrons ajouter nos propres tests (unit) pour vérifier sa fonctionnalité comme un utilitaire réutilisable. (Nous devons également déplacer le fichier car il n'est plus spécifique à LoginModule.)

Les tests unitaires ont encore de nombreux cas d'utilisation, tels que la nécessité de vérifier les sélecteurs, les crochets et les fonctions normaux réutilisables. Lorsque vous développez votre code, vous pouvez également trouver utile d'utiliser un développement axé sur les tests unitaires, même si vous déplacez cette logique jusqu'aux tests d'intégration plus tard.

De plus, les tests unitaires font un excellent travail de tests approfondis pour plusieurs entrées et cas d'utilisation. Par exemple, si mon formulaire doit afficher la validation en ligne pour divers scénarios (par exemple, un e-mail non valide, un mot de passe manquant, un mot de passe trop court), je couvrirai un cas représentatif dans le test d'intégration, puis creuserai dans le cas spécifique dans le test unitaire.

Autres avantages

Maintenant que nous sommes ici, je veux parler de certains conseils de syntaxe qui aident à garder mes tests d'intégration clairs et ordonnés.

Bloc d'attente effacer

Notre test doit tenir compte de la latence entre l'état de chargement et l'état réussi du loginmodule:

 const Button = Screen.getByRole ('Button');
FireEvent.Click (bouton);

attendre (bouton) .not.tobeIntheDocument (); // trop vite, le bouton est toujours là!
Copier après la connexion

Nous pouvons le faire en utilisant la fonction d'assistance Waitfor de la bibliothèque de tests DOM:

 const Button = Screen.getByRole ('Button');
FireEvent.Click (bouton);

attendre waitfor (() => {
 attendre (bouton) .not.tobeIntheDocument (); // ah, c'est beaucoup mieux});
Copier après la connexion

Mais que se passe-t-il si nous voulons tester d'autres projets? Il n'y a pas beaucoup de bons exemples sur Internet sur la façon de gérer cela, et dans les projets antérieurs, j'ai mis d'autres projets en dehors de WaitFor:

 // bouton d'attente attend Waitfor (() => {
 attendre (bouton) .not.tobeIntheDocument ();
});

// Testez ensuite le message de confirmation constanceText CONSTRIXText = GetByText ('connecté en tant que [e-mail protégé]');
attendre (ConfirmationText) .TobeIntheDocument ();
Copier après la connexion

Cela fonctionne, mais je n'aime pas ça car cela rend la condition du bouton spéciale, même si nous pouvons facilement changer l'ordre de ces déclarations:

 // attendre le message de confirmation Await WaitFor () => {
 const ConfirmationText = GetByText ('connecté en tant que [e-mail protégé]');
 attendre (ConfirmationText) .TobeIntheDocument ();
});

// Testez ensuite le bouton attendre (bouton) .not.tobeIntheDocument ();
Copier après la connexion

Il me semble qu'il vaut mieux regrouper tout ce qui concerne la même mise à jour dans le rappel WaitFor:

 attendre waitfor (() => {
 attendre (bouton) .not.tobeIntheDocument ();

 const ConfirmationText = Screen.getByText ('connecté en tant que [e-mail protégé]');
 attendre (ConfirmationText) .TobeIntheDocument ();
});
Copier après la connexion

J'aime vraiment cette technique pour des affirmations simples comme celle-ci, mais dans certains cas, cela peut ralentir les tests, en attendant un échec qui se produit immédiatement à l'extérieur de l'attente. Pour cet exemple, voir "plusieurs assertions dans un seul rappel d'attente" dans l'erreur commune de la bibliothèque de tests React.

Pour les tests contenant plusieurs étapes, nous pouvons utiliser plusieurs blocs d'attente successivement:

 const Button = Screen.getByRole ('Button');
const e-mail e-mail = screen.getByRole ('textbox', {name: 'email'});

// Remplissez le formulaire fireevent.change (champ de messagerie, {cible: {valeur: '[e-mail protégé]'}});

attendre waitfor (() => {
 // Vérifiez si le bouton est activé attendre (bouton) .not.tobedisabled ();
  attendre (bouton) .toHaveTextContent («soumettre»);
});

// Soumettez le formulaire fireevent.click (bouton);

attendre waitfor (() => {
 // Vérifiez si le bouton n'existe plus attendre (bouton) .not.tobeIntheDocument ();
});
Copier après la connexion

Si vous n'attendez qu'un seul élément apparaît, vous pouvez utiliser une requête findby à la place. Il utilise Waitfor en arrière-plan.

Les commentaires en ligne

Une autre meilleure pratique des tests consiste à écrire moins de tests plus longs; Cela vous permet de corréler les cas de test avec des processus utilisateur importants tout en gardant les tests isolés pour éviter un comportement inattendu. Je suis d'accord avec cette approche, mais elle peut poser un défi pour garder le code organisé et documenter le comportement requis. Nous avons besoin de futurs développeurs pour pouvoir revenir au test et comprendre ce qu'il fait, pourquoi il échoue, etc.

Par exemple, supposons que l'une de ces attentes commence à échouer:

 it ('gère un flux de connexion réussi', async () => {
 // masque le début du test pour la clarté

  attendre (bouton) .Tobedisabled ();
  attendre (bouton) .toHaveTextContent («Chargement ...»);


 attendre waitfor (() => {
  attendre (bouton) .not.tobeIntheDocument ();
  Attendez-vous (champ de messagerie) .not.tobeIntheDocument ();
  Attendez-vous (Passwordfield) .not.tobeIntheDocument ();


  const ConfirmationText = Screen.getByText ('connecté en tant que [e-mail protégé]');
  attendre (ConfirmationText) .TobeIntheDocument ();
 });
});
Copier après la connexion

Les développeurs qui examinent ce contenu ne peuvent pas facilement déterminer ce qui est testé, et il peut être difficile de déterminer si l'échec est un bogue (ce qui signifie que nous devons corriger le code) ou un changement de comportement (ce qui signifie que nous devons corriger le test).

Ma solution préférée est d'utiliser la syntaxe de test peu connue pour chaque test et d'ajouter un commentaire de style en ligne décrivant chaque comportement clé testé:

 TEST ('Connexion réussie', async () => {
 // masque le début du test pour la clarté

 // il définit le statut de chargement attendre (bouton) .tobedisabled ();
  attendre (bouton) .toHaveTextContent («Chargement ...»);


 attendre waitfor (() => {
  // il cache l'élément de forme attendre (bouton) .not.tobeIntheDocument ();
  Attendez-vous (champ de messagerie) .not.tobeIntheDocument ();
  Attendez-vous (Passwordfield) .not.tobeIntheDocument ();


  // Il affiche le texte de réussite et l'adresse e-mail constationxt xtext = screen.getByText ('connecté en tant que [e-mail protégé]');
  attendre (ConfirmationText) .TobeIntheDocument ();
 });
});
Copier après la connexion

Ces commentaires ne s'intègrent pas comme par magie à la plaisanterie, donc si vous rencontrez un échec, le nom de test défaillant correspondra aux paramètres que vous avez transmis à la balise de test, dans ce cas "Connexion réussie". Cependant, les messages d'erreur de Jest contiennent le code environnant, de sorte que ces commentaires informatiques aident toujours à identifier le comportement raté. Lorsque je ne supprime pas d'une attente, j'obtiens le message d'erreur suivant:

Pour obtenir des erreurs plus explicites, il existe un package appelé Jest-Expect-Message qui vous permet de définir des messages d'erreur pour chaque attente:

 attendez-vous (bouton, 'bouton est toujours dans le document'). not.tobeIntheDocument ();
Copier après la connexion

Certains développeurs préfèrent cette approche, mais je trouve cela un peu trop granulaire dans la plupart des cas, car il implique généralement plusieurs attentes.

Étapes suivantes pour l'équipe

Parfois, je souhaite que nous puissions faire des règles de linter pour les humains. Si c'est le cas, nous pouvons définir une règle de tests de préférence pour notre équipe et cela se termine.

Mais, hélas, nous devons trouver une solution plus similaire pour encourager les développeurs à choisir des tests d'intégration dans certains cas, tels que l'exemple de loginmodule que nous avons introduit plus tôt. Comme la plupart des choses, cela se résume à l'équipe discutant de votre stratégie de test, en accordant à ce qui a du sens pour le projet et - en mesure de le documenter dans ADR.

Lors de l'élaboration d'un plan de test, nous devons éviter une culture qui oblige les développeurs à rédiger des tests pour chaque fichier. Les développeurs doivent être en mesure de prendre des décisions de test éclairées en toute confiance sans se soucier de leur «entreprise». Le rapport de couverture de Jest peut aider à résoudre ce problème en fournissant une vérification de la santé mentale, même si les tests sont fusionnés au niveau de l'intégration.

Je ne me considère toujours pas comme un expert des tests d'intégration, mais faire cet exercice m'a aidé à décomposer un cas d'utilisation où les tests d'intégration offrent plus de valeur que les tests unitaires. J'espère que partager cela avec votre équipe ou faire des exercices similaires sur votre base de code vous guidera à intégrer des tests d'intégration dans votre flux de travail.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Vue 3 Vue 3 Apr 02, 2025 pm 06:32 PM

Il est sorti! Félicitations à l'équipe Vue pour l'avoir fait, je sais que ce fut un effort massif et une longue période à venir. Tous les nouveaux documents aussi.

Pouvez-vous obtenir des valeurs de propriété CSS valides du navigateur? Pouvez-vous obtenir des valeurs de propriété CSS valides du navigateur? Apr 02, 2025 pm 06:17 PM

J'ai eu quelqu'un qui écrivait avec cette question très légitime. Lea vient de bloguer sur la façon dont vous pouvez obtenir les propriétés CSS valides elles-mêmes du navigateur. C'est comme ça.

Un peu sur CI / CD Un peu sur CI / CD Apr 02, 2025 pm 06:21 PM

Je dirais que "Site Web" correspond mieux que "Application mobile" mais j'aime ce cadrage de Max Lynch:

Utilisation de Markdown et de la localisation dans l'éditeur de blocs WordPress Utilisation de Markdown et de la localisation dans l'éditeur de blocs WordPress Apr 02, 2025 am 04:27 AM

Si nous devons afficher la documentation à l'utilisateur directement dans l'éditeur WordPress, quelle est la meilleure façon de le faire?

Cartes empilées avec un positionnement collant et une pincée de sass Cartes empilées avec un positionnement collant et une pincée de sass Apr 03, 2025 am 10:30 AM

L'autre jour, j'ai repéré ce morceau particulièrement charmant sur le site Web de Corey Ginnivan où une collection de cartes se cassent les uns sur les autres pendant que vous faites défiler.

Comparaison des navigateurs pour une conception réactive Comparaison des navigateurs pour une conception réactive Apr 02, 2025 pm 06:25 PM

Il existe un certain nombre de ces applications de bureau où l'objectif montre votre site à différentes dimensions en même temps. Vous pouvez donc, par exemple, écrire

Comment utiliser la grille CSS pour les en-têtes et pieds de page collants Comment utiliser la grille CSS pour les en-têtes et pieds de page collants Apr 02, 2025 pm 06:29 PM

CSS Grid est une collection de propriétés conçues pour faciliter la mise en page qu'elle ne l'a jamais été. Comme tout, il y a un peu une courbe d'apprentissage, mais Grid est

Fontes variables de polices Google Fontes variables de polices Google Apr 09, 2025 am 10:42 AM

Je vois que Google Fonts a déployé un nouveau design (tweet). Comparé à la dernière grande refonte, cela semble beaucoup plus itératif. Je peux à peine faire la différence

See all articles