Composant React : introduction
React.js c'est la bibliothèque graphique crée par Facebook pour la création de front ends web interactives. Le composant React met en avance une approche basée sur des composants : une application est un ensemble de composants imbriqués qui React se charge de transformer en page web.
Dans cet article on parlera de comment écrire des tests (unitaires, mais pas que) pour des composants React. Pourquoi? Car un composant React, c'est du code Javascript. Un composant peut embarquer de la logique assez complexe. On écrit des tests pour s'assurer que notre code se comporte comme prévu, pour s'assurer que des modifications futures ne cassent pas des comportements vitaux, et pour éviter des régressions.
Avant de commencer: il y a quoi à tester?
Concrètement, un composant React est défini soit comme une fonction soit comme une classe. Un composant a un ensemble de propriétés (props) et un état local (state). Un composant embarque du code Javascript qui génère à partir de son état et de ses props un arbre d'éléments React. Les nœuds représentent des composants React et/ou des balises HTML (appelé DOM virtuel).
React se charge de mettre à jour le DOM de la page dès que les props ou le state d'un composant changent en comparant le DOM virtuel et le DOM réel. De ce fait, seul le sous ensemble de la page web qui a changé se met à jours. Ces concepts son illustrés dans la figure suivante.
Dans le cas où le composant est défini comme une fonction, un composant est une fonction qui prends en entrée des props et retourne un arbre de Il est donc stateless.
Dans l'exemple ci-dessous, on définit un composant appelé Badge avec la propriété (props) name. Une fois exécutée, la fonction retourne le rendu du composant. Le rendu du composant est composé d'une balise div avec le texte Hello {name}, this is your badge! à l'intérieur. La syntaxe {name} est utilisé pour afficher la valeur de la propriété name dans le code HTML du rendu du composant.
Un composant peut aussi être décrit comme une class Javascript. Dans ce cas, on pourra définir et manipulé l'état du composant. Où pourra aussi réagir à des changements d'état du composant dans son cycle de vie.
La figure suivante illustre le cycle de vie d'un composant React :
Sans expliquer en détails chaque étape du cycle de vie, en tant que développeur on peut écrire du code pour se brancher sur les étapes :
D'initialisation: de l'état EntryPoint à l'état Rendered;
De la décision du déclenchement d'une régénération du rendu graphique d'un composant en function des changements de props/états: les états PropsChanged et StateChanged;
De la régénération effective du rendu du composant: de l'état ShouldUpdate à l'état Rendered.
Ci-dessous, on montre comment décrire un composant React en tant que classe Javascript. De plus, on montre comment surcharger son comportement juste avant d'être monté sur le DOM.
La plupart du temps, voici ce qu'on va vouloir tester chez un component React:
Étant donnée l'état de ses props/state, quel arbre de composants un composant doit générer?
Comment se comporte le composant dans les différentes étapes de son cycle de vie?
Comment se comporte l'arbre de composants face à des événements UI?
Le but de cet article n'est pas de présenter une liste exhaustive des outils existants de test de composants React, mais de présenter les méthodes les plus utilisés. En général, un teste d'un composant React se fait en trois étapes: d'abord on crée une instance du composant, après on utilise l'instance pour générer un arbre DOM (virtuel ou réel) sur lequel on pourra écrire des asserts pour vérifier ses propriétés attendues.
L'objet qui prends en entrée une instance de composant React et génère un rendu DOM est un renderer. Dans cet article on verra les renderers disponibles dans l'API standard React pour tester des components.
Dans la section Pour aller plus loin vous trouverez des liens vers des articles plus complets, et d'autres APIs.
Mon composant React génère-t-il le bon arbre de sous éléments?
Pour tester ça, le plus simple c'est d'utiliser ce qu'on appelle le shallow rendering. Il s'agit de générer l'arbre du DOM virtuel du composant, mais en s'arrêtant au premier niveau d'imbrication (i.e. sans générer le rendu des sous composants). Cela nos donne un moyen pas cher de tester unitairement un composant.
Imaginons le composant suivant qui réutilise le composant Badge précédemment défini.
Ce composant affiche directement le prénom et nom de l'utilisateur (dans des balises HTML p) mais réutilise le composant Badge pour afficher un message de bienvenue à l'utilisateur. Lors qu'on écrit un teste unitaire, on se concentre sur l'arbre d'éléments généré par UserProfile, sans tester l'implémentation du composant Badge.
L'exemple suivant montre comment vérifier dans un test si une instance du composant Badge avec les bons props est bien générée par le composant UserProfile.
A-t-il le bon comportement dans chaque étape de son cycle de vie?
Le ShallowRenderer est très bien pour tester le rendu d'un composant, malheureusement, il n'implémente pas tout cycle de vie React. Pour cela, nous allons utiliser le TestRenderer. Il génère un rendu complet du composant (y compris de ses sous composants) et fournit des méthodes telles que update() et unmount() qui peuvent être utilisées pour simuler les mises à jour et le démontage d'un composant.
Ici, on a un composant auquel on associe des fonctions Javascript à appeler pendant différentes étapes du cycle de vie :
Pour le test réalisé ci-dessous, on crée un premier rendu (render) du composant (component). Après on le mets à jour, avec l'aide d'une autre instance (changedComponent) du composant avec la même key et enfin, on démonte le composant.
Se comporte-t-il bien face à des événements UI?
Lorsque l'on veut écrire ce genre de tests, il faudra utiliser un type de renderer different de ceux qu'on a utilisé dans les sections précédentes. Il nous faut un renderer qui monte le composant testé dans un DOM (vrai ou simulé) et qui est donc capable de lui envoyer des événements. Dans cet article on utilisera le renderer renderIntoDocument() pour générer le rendu sur un DOM et l'utilitaire Simulate pour envoyer des événements à un composant React.
Considère le composant suivant, qui représente un compteur de clicks:
Si on veut voir si le button '+' fonctionne correctement, on simulera un événement click avec l'outil Simulate. On testera si le compteur de clicks était bien à 0 avant le click, et 1 après. Remarquez qu'on importe ReactDOM qui nous simulera un DOM sur lequel les composants seront crées.
Pour aller plus loin
Pour conclure, on a juste présenté une introduction rapide sur pourquoi et comment tester des composants React et quelques méthodes de mise du composant en situation d'être testé. Dans cet article on n'a parlé que des outils de l'API standard React. Pour aller plus loin, je vous propose quelques articles plus complets et quelques bibliothèques utiles:
L'API AirBnB pour écrire des tests de components react enzyme
- L'API React Testing Library pour tester des components react en suivant une approche complètement différente de celle présenté dans cet article
Des articles plus détaillés sur comment écrire des tests pour des components react: how react components make ui test easy et getting started with tdd in react
Un très bon article sur comment faire du TDD en react: react + tdd = (heart)
Une bonne discussion sur pour quoi ne pas tester des composants graphiques :) ça peut aider à decider de quels comportements tester chez un composant React: When TDD doens't Work
Envie de poursuivre votre lecture ? Découvrez les autres articles de notre blog !