Test de composants dans React en utilisant Jest et Enzyme

Ceci est la deuxième partie de la série sur le test des composants dans React. Si vous avez une expérience préalable avec Jest, vous pouvez sauter et utiliser le code GitHub comme point de départ. 

Dans l'article précédent, nous avons présenté les principes de base et les idées sous-jacentes au développement piloté par les tests. Nous avons également configuré l'environnement et les outils nécessaires à l'exécution des tests dans React. Le jeu d'outils comprenait Jest, ReactTestUtils, Enzyme et react-test-renderer. 

Nous avons ensuite écrit quelques tests pour une application de démonstration utilisant ReactTestUtils et découvert ses inconvénients par rapport à une bibliothèque plus robuste comme Enzyme..

Dans cet article, nous approfondirons la compréhension du test des composants dans React en rédigeant des tests plus pratiques et plus réalistes. Vous pouvez vous rendre sur GitHub et cloner mon référentiel avant de commencer.

Démarrer avec l'API Enzyme

Enzyme.js est une bibliothèque open-source maintenue par Airbnb. Elle constitue une excellente ressource pour les développeurs de React. Il utilise l'API ReactTestUtils sous-jacent, mais contrairement à ReactTestUtils, Enzyme propose une API de haut niveau et une syntaxe facile à comprendre. Installez Enzyme si vous ne l'avez pas déjà fait.

L'API Enzyme exporte trois types d'options de rendu:

  1. rendu superficiel
  2. rendu DOM complet
  3. rendu statique

Rendu peu profond est utilisé pour rendre un composant particulier de manière isolée. Les composants enfants ne seront pas rendus et vous ne pourrez donc pas affirmer leur comportement. Si vous allez vous concentrer sur les tests unitaires, vous allez adorer ça. Vous pouvez rendre un composant peu profond comme ceci:

importer peu profond de 'enzyme'; importer ProductHeader à partir de './ProductHeader'; // Exemple plus concret ci-dessous. composant const = peu profond () 

Rendu complet du DOM génère un DOM virtuel du composant à l'aide d'une bibliothèque appelée jsdom. Vous pouvez bénéficier de cette fonctionnalité en remplaçant le peu profond() méthode avec monter() dans l'exemple ci-dessus. L'avantage évident est que vous pouvez également rendre les composants enfants. Si vous voulez tester le comportement d’un composant avec ses enfants, vous devriez utiliser cette. 

Rendu statique est utilisé pour rendre les composants de réaction en HTML statique. Il est implémenté à l'aide d'une bibliothèque appelée Cheerio, et vous pouvez en apprendre plus à ce sujet dans la documentation.. 

Revisiter nos tests précédents

Voici les tests que nous avons écrits dans le dernier tutoriel:

src / components / __ tests __ / ProductHeader.test.js

importer ReactTestUtils à partir de 'react-dom / test-utils'; // ES6 describe ('ProductHeader Component', () => it ('a une balise h2', () => const Component = ReactTestUtils .renderIntoDocument () var node = ReactTestUtils .findRenderedDOMComponentWithTag (composant, 'h2'); ); it ('a une classe de titre', () => const composant = ReactTestUtils .renderIntoDocument () var node = ReactTestUtils .findRenderedDOMComponentWithClass (composant, 'titre'); ))

Le premier test vérifie si le ProducerHeader composant a un

balise, et la seconde trouve si elle a une classe CSS nommée Titre. Le code est difficile à lire et à comprendre. 

Voici les tests réécrits avec Enzyme.

src / components / __ tests __ / ProductHeader.test.js

importer peu profond de 'enzyme' decrire ('Composant ProductHeader', () => it ('a une balise h2', () => const composant = peu profond () var node = composant.find ('h2'); expect (node.length) .toEqual (1); ); it ('a une classe de titre', () => const composant = shallow () var node = composant.find ('h2'); expect (node.hasClass ('title')). toBeTruthy (); ))

Tout d'abord, j'ai créé un DOM peu profond rendu de la  composant utilisant peu profond() et stocké dans une variable. Ensuite, j'ai utilisé le .trouver() méthode pour trouver un noeud avec la balise 'h2'. Il interroge le DOM pour savoir s'il existe une correspondance. Puisqu'il n'y a qu'une seule instance du nœud, on peut supposer en toute sécurité que node.length sera égal à 1.

Le deuxième test est très similaire au premier. le hasClass ('title') La méthode retourne si le noeud actuel a un nom du cours prop avec la valeur 'titre'. Nous pouvons vérifier la véracité en utilisant toBeTruthy ().  

Exécutez les tests en utilisant test de fil, et les deux tests devraient passer. 

Bien joué! Il est maintenant temps de refactoriser le code. Ceci est important du point de vue du testeur car les tests lisibles sont plus faciles à gérer. Dans les tests ci-dessus, les deux premières lignes sont identiques pour les deux tests. Vous pouvez les refactoriser en utilisant un beforeEach () une fonction. Comme son nom l'indique, le avant chaque la fonction est appelée une fois avant que chaque spécification d'un bloc de description soit exécutée. 

Vous pouvez passer une fonction de flèche à beforeEach () comme ça.

src / components / __ tests __ / ProductHeader.test.js

importer shallow depuis 'enzyme' decrire ('ProductHeader Component', () => let composant, noeud; // Jest beforeEach () beforeEach ((() => composant = shallow)))) beforeEach ((() => node = composant.find ('h2'))) it ('a une balise h2', () => expect (node) .toBeTruthy ()); it ('a une classe de titre', () => expect (node.hasClass ('title')). toBeTruthy ()))

Écrire des tests unitaires avec Jest et Enzyme

Écrivons quelques tests unitaires pour le Détails du produit composant. C'est un composant de présentation qui affiche les détails de chaque produit individuel. 

Nous allons tester la section qui est mise en évidence

Le test unitaire va tenter d’affirmer les hypothèses suivantes:

  • Le composant existe et les accessoires sont transmis.
  • Les accessoires tels que le nom du produit, la description et la disponibilité sont affichés.
  • Un message d'erreur est affiché lorsque les accessoires sont vides.

Voici la structure de base du test. La première beforeEach () stocke les données de produit dans une variable et la seconde monte le composant.

src / composants / __ tests __ / ProductDetails.test.js

describe ("composant ProductDetails", () => composant var, produit; beforeEach ((= =) product = id: 1, nom: "NIKE Liteforce Blue Sneakers", description: "Lorem ipsum.", statut: 'Available';) beforeEach (() => composant = mount () ) il ('test # 1', () => ))

Le premier test est facile:

it ('devrait exister', () => expect (composant) .toBeTruthy (); expect (composant.props (). produit) .toEqual (produit);)

Ici nous utilisons le accessoires () méthode qui est pratique pour obtenir les accessoires d'un composant.

Pour le deuxième test, vous pouvez interroger les éléments par leur nom de classe, puis vérifier si le nom du produit, sa description, etc. font partie de celui de cet élément. innerText

 it ('devrait afficher les données du produit lorsque les accessoires sont passés', () => let title = composant.find ('. product-title'); expect (title.text ()). toEqual (product.name); let description = composant.find ('. description-produit'); expect (description.text ()). toEqual (product.description);) 

le texte() Cette méthode est particulièrement utile dans ce cas pour récupérer le texte intérieur d’un élément. Essayez d’écrire une attente pour le État du produit() et voir si tous les tests passent.

Pour le test final, nous allons monter le Détails du produit composant sans accessoires. Ensuite, nous allons chercher une classe nommée '.product-error' et vérifier si elle contient le texte "Désolé, le produit n'existe pas".

 it ('devrait afficher une erreur quand les accessoires ne sont pas passés', () => / * composant sans accessoires * / composant = mount () let node = composant.find ('. product-error'); expect (node.text ()). toEqual ('Désolé, le produit n'existe pas'); ) 

C'est tout. Nous avons testé avec succès le composant en isolation. Les tests de ce type sont appelés tests unitaires.

Test des rappels à l'aide de talons et d'espions

Nous venons d'apprendre à tester les accessoires. Mais pour véritablement tester un composant de manière isolée, vous devez également tester les fonctions de rappel. Dans cette section, nous allons écrire des tests pour le Liste de produits composant et créer des stubs pour les fonctions de rappel en cours de route. Voici les hypothèses que nous devons affirmer.

  1. Le nombre de produits répertoriés doit être équivalent au nombre d'objets que le composant reçoit sous forme d'accessoires..
  2. En cliquant sur devrait invoquer la fonction de rappel.

Créons un beforeEach () fonction qui remplit les données de produits fictifs pour nos tests.

src / components / __ tests __ / ProductList.test.js

 beforeEach (() => productData = [id: 1, nom: 'NIKE Liteforce Blue Sneakers', description: 'Lorem ipsu.', statut: 'Disponible', // Omis pour des raisons de brièveté])

Maintenant, montons notre composant dans un autre beforeEach () bloc.

beforeEach (() => handleProductClick = jest.fn (); composant = monter (  ) )

le Liste de produits reçoit les données sur le produit via des accessoires. En plus de cela, il reçoit un rappel du parent. Bien que vous puissiez écrire des tests pour la fonction de rappel du parent, ce n'est pas une bonne idée si votre objectif est de vous en tenir aux tests unitaires. Puisque la fonction de rappel appartient au composant parent, l'intégration de la logique du parent compliquera les tests. Au lieu de cela, nous allons créer une fonction stub.

Qu'est-ce qu'un bout? 

Un stub est une fonction factice qui prétend être une autre fonction. Cela vous permet de tester indépendamment un composant sans importer des composants parents ou enfants. Dans l'exemple ci-dessus, nous avons créé une fonction stub appelée handleProductClick en invoquant jest.fn ()

Maintenant, nous avons juste besoin de trouver tous les éléments dans le DOM et simuler un clic sur le premier nœud. Après avoir cliqué, nous vérifierons si handleProductClick () a été invoqué. Si oui, il est juste de dire que notre logique fonctionne comme prévu.

it ('doit appeler selectProduct lorsque vous cliquez dessus', () => const firstLink = composant.find ('a'). first (); firstLink.simulate ('click'); expect (handleProductClick.mock.calls.length) .toEqual (1);))

Enzyme vous permet de simuler facilement des actions de l'utilisateur telles que des clics à l'aide de simuler() méthode. handlerProductClick.mock.calls.length renvoie le nombre de fois où la fonction fictive a été appelée. Nous nous attendons à ce qu'il soit égal à 1.

L'autre test est relativement facile. Vous pouvez utiliser le trouver() méthode pour récupérer tous nœuds dans le DOM. Le nombre de les nœuds doivent être égaux à la longueur du tableau productData que nous avons créé précédemment. 

 it ('devrait afficher tous les articles du produit', () => let links = composant.find ('a'); expect (links.length) .toEqual (productData.length);) 

Test de l'état, du cycle de vie et de la méthode du composant

Ensuite, nous allons tester la ProductContainer composant. Il comporte un état, un cycle de vie et une méthode de classe. Voici les affirmations qui doivent être vérifiées:

  1. composantDidMount est appelé exactement une fois.
  2. L'état du composant est renseigné après le montage du composant..
  3. le handleProductClick () La méthode doit mettre à jour l'état lorsqu'un ID de produit est transmis en tant qu'argument.

Pour vérifier si composantDidMount a été appelé, nous allons l'espionner. Contrairement à un stub, un espion est utilisé lorsque vous devez tester une fonction existante. Une fois que l'espion est défini, vous pouvez écrire des assertions pour confirmer si la fonction a été appelée.

Vous pouvez espionner une fonction comme suit:

src / components / __ tests __ / ProductContainer.test.js

 it ('devrait appeler composantDidMount une fois', () => composantDidMountSpy = spyOn (ProductContainer.prototype, 'composantDidMount'); // à terminer);

Le premier paramètre à jest.spyOn est un objet qui définit le prototype de la classe que nous espionnons. Le second est le nom de la méthode que nous voulons espionner. 

Rendez maintenant le composant et créez une assertion pour vérifier si l'espion s'appelait.

 composant = peu profond () expect (composantDidMountSpy) .toHaveBeenCalledTimes (1);

Pour vérifier que l’état du composant est renseigné après le montage du composant, nous pouvons utiliser Enzyme. Etat() méthode pour tout récupérer dans l'état. 

it ('devrait peupler l'état', () => composant = shallow () expect (composant.state (). productList.length) .toEqual (4))

Le troisième est un peu délicat. Nous devons vérifier que handleProductClick fonctionne comme prévu. Si vous vous dirigez vers le code, vous verrez que le handleProductClick () méthode prend un ID de produit en entrée, puis met à jour this.state.selectedProduct avec les détails de ce produit. 

Pour tester cela, nous devons invoquer la méthode du composant, et vous pouvez le faire en appelant composant.instance (). handleProductClick (). Nous allons passer un exemple d'ID de produit. Dans l'exemple ci-dessous, nous utilisons l'identifiant du premier produit. Ensuite, nous pouvons tester si l'état a été mis à jour pour confirmer que l'assertion est vraie. Voici le code complet:

 it ('devrait avoir une méthode de travail appelée handleProductClick', () => let firstProduct = productData [0] .id; composant = shallow () composant.instance (). handleProductClick (firstProduct); expect (composant.state (). selectedProduct) .toEqual (productData [0]); ) 

Nous avons écrit 10 tests, et si tout se passe bien, voici ce que vous devriez voir:

Résumé

Phew! Nous avons couvert presque tout ce que vous devez savoir pour commencer à écrire des tests dans React en utilisant Jest and Enzyme. Le moment est peut-être venu de se rendre sur le site Web d'Enzyme pour examiner de plus près leur API..

Que pensez-vous de la rédaction de tests dans React? J'aimerais les entendre dans les commentaires.