L’auteur a sélectionné Vets Who Code pour recevoir une donation dans le cadre du programme Write for DOnations.
Introduction
Obtenir une couverture de test solide est impératif pour renforcer la confiance dans votre application web. Jest est un exécuteur de tests JavaScript qui fournit des ressources pour écrire et exécuter des tests. React Testing Library propose un ensemble d’helpers de test qui structurent vos tests en fonction des interactions utilisateur plutôt que des détails d’implémentation des composants. Jest et React Testing Library sont pré-packagés avec Create React App et respectent le principe directeur selon lequel les tests d’applications doivent ressembler à la façon dont le logiciel sera utilisé.
Dans ce tutoriel, vous testerez le code asynchrone et les interactions dans un projet exemple contenant divers éléments d’interface utilisateur. Vous utiliserez Jest pour écrire et exécuter des tests unitaires, et vous implémenterez React Testing Library en tant que bibliothèque d’aide DOM (Document Object Model) pour gérer les interactions avec les composants.
Prérequis
Pour compléter ce tutoriel, vous aurez besoin de :
-
Node.js version 14 ou supérieure installée sur votre machine locale. Pour installer Node.js sur macOS ou Ubuntu 18.04, suivez les étapes dans la section Comment installer Node.js et créer un environnement de développement local sur macOS ou la section Installation à l’aide d’un PPA de Comment installer Node.js sur Ubuntu 18.04.
-
npm
version 5.2 ou supérieure sur votre machine locale, dont vous aurez besoin pour utiliser Create React App etnpx
dans le projet d’exemple. Si vous n’avez pas installénpm
avecNode.js
, faites-le maintenant. Pour Linux, utilisez la commandesudo apt install npm
.- Pour que les packages
npm
fonctionnent dans ce tutoriel, installez le packagebuild-essential
. Pour Linux, utilisez la commandesudo apt install build-essential
.
- Pour que les packages
-
Git installé sur votre machine locale. Vous pouvez vérifier si Git est installé sur votre ordinateur ou suivre le processus d’installation pour votre système d’exploitation avec Comment installer Git sur Ubuntu 20.04.
-
Connaissance de React, que vous pouvez développer avec la série Comment Coder en React.js. Comme le projet d’exemple est initialisé avec Create React App, vous n’avez pas besoin de l’installer séparément.
-
Une certaine familiarité avec Jest en tant que runner ou framework de test est utile mais non requise. Comme Jest est pré-installé avec Create React App, vous n’avez pas besoin de l’installer séparément.
Étape 1 — Configuration du Projet
À cette étape, vous allez cloner un projet d’exemple et lancer la suite de tests. Le projet d’exemple utilise trois principaux outils : Create React App, Jest et React Testing Library. Create React App est utilisé pour amorcer une application React monopage. Jest est utilisé comme exécuteur de tests, et React Testing Library fournit des aides de test pour structurer les tests autour des interactions utilisateur.
Pour commencer, vous allez cloner une application React pré-construite depuis GitHub. Vous travaillerez avec l’application Doggy Directory, qui est un projet d’exemple qui exploite l’API Dog API pour construire un système de recherche et d’affichage pour une collection d’images de chiens basé sur une race spécifique.
Pour cloner le projet depuis Github, ouvrez votre terminal et exécutez la commande suivante :
Vous verrez une sortie similaire à ceci :
OutputCloning into 'doggy-directory'...
remote: Enumerating objects: 64, done.
remote: Counting objects: 100% (64/64), done.
remote: Compressing objects: 100% (48/48), done.
remote: Total 64 (delta 21), reused 55 (delta 15), pack-reused 0
Unpacking objects: 100% (64/64), 228.16 KiB | 3.51 MiB/s, done.
Changez vers le dossier doggy-directory
:
Installez les dépendances du projet :
La commande npm install
installera toutes les dépendances du projet définies dans le fichier package.json
.
Après avoir installé les dépendances, vous pouvez soit afficher la version déployée de l’application ou vous pouvez exécuter l’application localement avec la commande suivante :
Si vous choisissez d’exécuter l’application localement, elle s’ouvrira sur http://localhost:3000/
. Vous verrez la sortie suivante dans le terminal :
OutputCompiled successfully!
You can now view doggy-directory in the browser.
Local: http://localhost:3000
On Your Network: http://network_address:3000
Après le lancement, la page d’accueil de l’application ressemblera à ceci :
Les dépendances du projet ont été installées et l’application est maintenant en cours d’exécution. Ensuite, ouvrez un nouveau terminal et lancez les tests avec la commande suivante :
La commande npm test
lance les tests en mode interactif avec Jest comme exécuteur de tests. En mode veille, les tests se relancent automatiquement après chaque modification de fichier. Les tests s’exécuteront à chaque modification de fichier et vous informeront si cette modification a réussi les tests.
Après avoir exécuté npm test
pour la première fois, vous verrez cette sortie dans le terminal :
OutputNo tests found related to files changed since last commit.
Press `a` to run all tests, or run Jest with `--watchAll`.
Watch Usage
› Press a to run all tests.
› Press f to run only failed tests.
› Press q to quit watch mode.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press Enter to trigger a test run.
Maintenant que vous avez l’application exemple et la suite de tests en cours d’exécution, vous pouvez commencer les tests avec la page d’accueil.
Étape 2 — Tester la page d’accueil
Par défaut, Jest recherchera les fichiers avec le suffixe .test.js
et les fichiers avec le suffixe .js
dans les dossiers __tests__
. Lorsque vous apportez des modifications aux fichiers de test pertinents, elles seront détectées automatiquement. À mesure que les cas de test sont modifiés, la sortie sera automatiquement mise à jour. Le fichier de test préparé pour le projet d’exemple doggy-directory
est configuré avec un code minimal avant d’ajouter des paradigmes de test. À cette étape, vous écrirez des tests pour vérifier que la page d’accueil de l’application se chargera avant d’effectuer une recherche.
Ouvrez src/App.test.js
dans votre éditeur pour voir le code suivant :
A minimum of one test block is required in each test file. Each test block accepts two required parameters: the first argument is a string representing the name of the test case; the second argument is a function that holds the expectations of the test.
À l’intérieur de la fonction, il y a une méthode render
que React Testing Library fournit pour rendre votre composant dans le DOM. Avec le composant que vous souhaitez tester rendu dans le DOM de l’environnement de test, vous pouvez maintenant commencer à écrire du code pour assert sur la fonctionnalité attendue.
Vous ajouterez un bloc de test à la méthode render
qui testera si la page de destination est rendue de manière précise avant que des appels API ou des sélections ne soient effectués. Ajoutez le code surligné sous la méthode render
:
La fonction expect
est utilisée chaque fois que vous souhaitez vérifier un résultat particulier, et elle accepte un argument unique représentant la valeur que votre code produit. La plupart des fonctions expect
sont associées à une fonction matcher pour affirmer quelque chose à propos d’une valeur particulière. Pour la plupart de ces assertions, vous utiliserez des matchers supplémentaires fournis par jest-dom pour faciliter la vérification des aspects courants trouvés dans le DOM. Par exemple, .toHaveTextContent
est le matcher pour la fonction expect
dans la première ligne, tandis que getByRole("heading")
est le sélecteur pour récupérer l’élément DOM.
React Testing Library fournit l’objet screen
comme un moyen pratique d’accéder aux requêtes pertinentes nécessaires pour affirmer contre l’environnement de test DOM. Par défaut, React Testing Library fournit des requêtes qui vous permettent de localiser des éléments dans le DOM. Il existe trois catégories principales de requêtes:
getBy*
(le plus couramment utilisé)queryBy*
(utilisé lors du test de l’absence d’un élément sans générer d’erreur)findBy*
(utilisé lors du test de code asynchrone)
Chaque type de requête sert un but spécifique qui sera défini plus tard dans le tutoriel. Dans cette étape, vous vous concentrerez sur la requête getBy*
, qui est le type de requête le plus courant. Pour voir une liste exhaustive des différentes variations de requêtes, vous pouvez consulter la feuille de triche des requêtes de React.
Voici une image annotée de la page d’accueil de l’annuaire des chiens indiquant chaque section que le premier test (sur le rendu de la page d’accueil) couvre :
Chaque fonction expect
vérifie les éléments suivants (comme indiqué dans l’image annotée ci-dessus) :
- Vous vous attendez à ce que l’élément avec le rôle d’en-tête ait une correspondance partielle avec Annuaire des chiens.
- Vous vous attendez à ce que l’entrée de sélection ait une valeur d’affichage exacte de Sélectionnez une race.
- Vous vous attendez à ce que le bouton Rechercher soit désactivé car aucune sélection n’a été effectuée.
- Vous vous attendez à ce que l’image de remplacement soit présente dans le document car aucune recherche n’a été effectuée.
Lorsque vous avez terminé, enregistrez le fichier src/App.test.js
. Comme les tests s’exécutent en mode surveillance, les modifications seront enregistrées automatiquement. Si les modifications ne sont pas enregistrées automatiquement, vous devrez peut-être arrêter et redémarrer la suite de tests.
Maintenant, lorsque vous regardez vos tests dans le terminal, vous verrez la sortie suivante:
Output PASS src/App.test.js
✓ renders the landing page (172 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.595 s, estimated 5 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
À cette étape, vous avez écrit un test initial pour vérifier la vue de rendu initiale de la page d’accueil du répertoire des chiens. À la prochaine étape, vous apprendrez comment simuler un appel API pour tester du code asynchrone.
Étape 3 — Simulation de la méthode fetch
À cette étape, vous examinerez une approche pour simuler la méthode fetch
de JavaScript. Bien qu’il existe de nombreuses façons d’y parvenir, cette implémentation utilisera les méthodes spyOn
et mockImplementation
de Jest.
Lorsque vous dépendez d’API externes, il est possible que leur API soit hors service ou mette du temps à renvoyer une réponse. Simuler la méthode fetch
fournit un environnement cohérent et prévisible, vous donnant plus de confiance dans vos tests. Un mécanisme de simulation d’API est nécessaire pour exécuter correctement des tests qui utilisent une API externe.
Remarque : Dans le but de simplifier ce projet, vous allez simuler la méthode fetch. Cependant, il est recommandé d’utiliser une solution plus robuste comme Mock Service Worker (MSW) pour simuler du code asynchrone pour des bases de code plus grandes et prêtes pour la production.
Ouvrez src/mocks/mockFetch.js
dans votre éditeur pour examiner le fonctionnement de la méthode mockFetch
:
La méthode mockFetch
renvoie un objet qui ressemble étroitement à la structure de ce qu’un appel fetch
renverrait en réponse aux appels d’API dans l’application. La méthode mockFetch
est nécessaire pour tester la fonctionnalité asynchrone dans deux domaines de l’application Doggy Directory : la liste déroulante de sélection qui peuple la liste des races et l’appel d’API pour récupérer des images de chiens lorsqu’une recherche est effectuée.
Fermez src/mocks/mockFetch.js
. Maintenant que vous comprenez comment la méthode mockFetch
sera utilisée dans vos tests, vous pouvez l’importer dans votre fichier de test. La fonction mockFetch
sera passée en argument à la méthode mockImplementation
et sera ensuite utilisée comme implémentation factice de l’API fetch.
Dans src/App.test.js
, ajoutez les lignes de code surlignées pour importer la méthode mockFetch
:
Ce code configurera et démontera l’implémentation simulée afin que chaque test parte d’une base équitable.
jest.spyOn(window, "fetch");
crée une fonction simulée qui suivra les appels à la méthode fetch
attachée à la variable globale window dans le DOM.
.mockImplementation(mockFetch);
accepte une fonction qui sera utilisée pour mettre en œuvre la méthode de simulation. Parce que cette commande remplace l’implémentation originale de fetch
, elle s’exécutera chaque fois que fetch
est appelé dans le code de l’application.
Une fois terminé, enregistrez le fichier src/App.test.js
.
Maintenant, lorsque vous regardez vos tests dans le terminal, vous recevrez la sortie suivante :
Output console.error
Warning: An update to App inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at App (/home/sammy/doggy-directory/src/App.js:5:31)
18 | })
19 | .then((json) => {
> 20 | setBreeds(Object.keys(json.message));
| ^
21 | });
22 | }, []);
23 |
...
PASS src/App.test.js
✓ renders the landing page (429 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.178 s, estimated 2 s
Ran all test suites related to changed files.
Le avertissement vous indique qu’une mise à jour de l’état s’est produite quand ce n’était pas attendu. Cependant, la sortie indique également que les tests ont simulé avec succès la méthode fetch
.
Dans cette étape, vous avez simulé la méthode fetch
et incorporé cette méthode dans une suite de tests. Bien que le test réussisse, vous devez toujours traiter l’avertissement.
Étape 4 — Correction de l’avertissement act
Dans cette étape, vous apprendrez comment corriger l’avertissement act
qui est apparu après les modifications de l’étape 3.
L’avertissement act
se produit car vous avez simulé la méthode fetch
, et lorsque le composant se monte, il effectue un appel API pour récupérer la liste des races. La liste des races est stockée dans une variable d’état qui remplit l’élément option
dans l’entrée select.
L’image ci-dessous montre à quoi ressemble l’entrée select après qu’un appel API réussi a été effectué pour remplir la liste des races :
Le avertissement est déclenché car l’état est défini après que le bloc de test a fini de rendre le composant.
Pour résoudre ce problème, ajoutez les modifications surlignées au cas de test dans src/App.test.js
:
Le mot-clé async
indique à Jest que le code asynchrone s’exécute en conséquence de l’appel API qui se produit lorsque le composant est monté.
A new assertion with the findBy
query verifies that the document contains an option with the value of husky
. findBy
queries are used when you need to test asynchronous code that is dependent on something being in the DOM after a period of time. Because the findBy
query returns a promise that gets resolved when the requested element is found in the DOM, the await
keyword is used within the expect
method.
Lorsque vous avez terminé, enregistrez les modifications apportées dans src/App.test.js
.
Avec les nouvelles additions, vous verrez maintenant que l’avertissement act
n’est plus présent dans vos tests :
Output PASS src/App.test.js
✓ renders the landing page (123 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.942 s, estimated 2 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
Dans cette étape, vous avez appris comment résoudre l’avertissement act
qui peut survenir lors du travail avec du code asynchrone. Ensuite, vous ajouterez un deuxième cas de test pour vérifier les fonctionnalités interactives de l’application Répertoire Canin.
Étape 5 — Tester la fonction de recherche
Dans la dernière étape, vous écrirez un nouveau cas de test pour vérifier la fonctionnalité de recherche et d’affichage d’image. Vous utiliserez une variété de requêtes et de méthodes API pour obtenir la couverture de test appropriée.
Retournez au fichier src/App.test.js
dans votre éditeur. En haut du fichier, importez la bibliothèque compagnon user-event
et la méthode asynchrone waitForElementToBeRemoved
dans le fichier de test avec les commandes surlignées:
Vous utiliserez ces importations plus tard dans cette section.
Après la méthode test()
initiale, ajoutez un nouveau bloc de test asynchrone et rendez le composant App
avec le bloc de code suivant:
Avec le composant rendu, vous pouvez maintenant ajouter des fonctions qui vérifient les fonctionnalités interactives de l’application Doggy Directory.
Toujours dans src/App.test.js
, ajoutez les blocs de code surlignés dans la deuxième méthode test()
:
La section surlignée ci-dessus simule la sélection d’une race de chien et vérifie que la bonne valeur est affichée.
La requête getByRole
récupère l’élément sélectionné et l’assigne à la variable select
.
Similaire à la façon dont vous avez corrigé l’avertissement act
à l’étape 4, utilisez la requête findByRole
pour attendre que l’option cattledog
apparaisse dans le document avant de procéder à d’autres assertions.
L’objet userEvent
importé précédemment simule des interactions utilisateur courantes. Dans cet exemple, la méthode selectOptions
sélectionne l’option cattledog
pour laquelle vous avez attendu à la ligne précédente.
La dernière ligne vérifie que la variable select
contient bien la valeur cattledog
sélectionnée précédemment.
La prochaine section que vous ajouterez au bloc test()
JavaScript initiara la demande de recherche pour trouver des images de chiens en fonction de la race sélectionnée et confirmera la présence d’un état de chargement.
Ajoutez les lignes en surbrillance :
La requête getByRole
localise le bouton de recherche et l’attribue à la variable searchBtn
.
Le matcher toBeDisabled
de jest-dom vérifiera que le bouton de recherche n’est pas désactivé lorsqu’une sélection de race est effectuée.
La méthode click
sur l’objet userEvent
simule le clic sur le bouton de recherche.
La fonction waitForElementToBeRemoved
importée précédemment est une fonction auxiliaire asynchrone qui attend l’apparition et la disparition du message Loading pendant que l’appel à l’API de recherche est en cours. queryByText
à l’intérieur du rappel waitForElementToBeRemoved
vérifie l’absence d’un élément sans générer d’erreur.
L’image ci-dessous montre l’état de chargement qui s’affichera lorsqu’une recherche est en cours :
Ensuite, ajoutez le code JavaScript suivant pour valider l’affichage de l’image et le compte des résultats :
La requête getAllByRole
sélectionnera toutes les images de chien et les affectera à la variable dogImages
. La variante *AllBy*
de la requête renvoie un tableau contenant plusieurs éléments correspondant au rôle spécifié. La variante *AllBy*
diffère de la variante ByRole
, qui ne peut renvoyer qu’un seul élément.
La mise en œuvre fetch
simulée contenait deux URL d’image dans la réponse. Avec le matcher toHaveLength
de Jest, vous pouvez vérifier qu’il y a deux images affichées.
La requête getByText
vérifiera que le bon nombre de résultats apparaît dans le coin droit.
Deux assertions utilisant les matchers toHaveAccessibleName
vérifient que le texte alternatif approprié est associé aux images individuelles.
A completed search displaying images of the dog based on the breed selected along with the number of results found will look like this:
Lorsque vous combinez toutes les parties du nouveau code JavaScript, le fichier App.test.js
ressemblera à ceci :
Enregistrez les modifications apportées dans src/App.test.js
.
Lorsque vous examinez vos tests, la sortie finale dans le terminal aura maintenant la sortie suivante :
Output PASS src/App.test.js
✓ renders the landing page (273 ms)
✓ should be able to search and display dog image results (123 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.916 s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
Dans cette dernière étape, vous avez ajouté un test qui vérifie les fonctionnalités de recherche, de chargement et d’affichage de l’application Doggy Directory. Avec l’assertion finale écrite, vous savez maintenant que votre application fonctionne.
Conclusion
Tout au long de ce tutoriel, vous avez écrit des cas de test en utilisant Jest, React Testing Library et les matchers jest-dom. En construisant de manière progressive, vous avez écrit des tests basés sur la manière dont un utilisateur interagit avec l’interface utilisateur. Vous avez également appris les différences entre les requêtes getBy*
, findBy*
et queryBy*
, ainsi que comment tester le code asynchrone.
Pour en savoir plus sur les sujets mentionnés ci-dessus, consultez la documentation officielle de Jest, React Testing Library et jest-dom. Vous pouvez également lire le Common Mistakes with React Testing Library de Kent C. Dodd pour en savoir plus sur les meilleures pratiques lors de l’utilisation de React Testing Library. Pour plus d’informations sur l’utilisation des tests snapshot dans une application React, consultez How To Write Snapshot Tests.