React : obtenir 100 % de couverture de code facilement

Just-Tech-IT
Just-Tech-IT
Published in
5 min readMar 11, 2020

Par Jérôme BOUKORRAS

Pour beaucoup de développeurs et développeuses, écrire des tests unitaires relève de la corvée. On le fait en trainant les pieds parce que l’on y est obligé. La plupart du temps, on trouve ça trop compliqué ou alors pas suffisamment intéressant, ce qui se traduit par des tests bâclés, non pertinents et une couverture de code trop faible. Il est pourtant facile de couvrir le code de composants React à 100 % sans trop de contraintes, en utilisant simplement Jest et Enzyme.

Oubliez les snapshots !

L’utilisation de snapshots est un moyen très rapide et facile de couvrir le code d’un composant. Seulement leur utilité reste à prouver. Quels cas cherche-t-on réellement à couvrir avec un snapshot ? Une modification du balisage HTML, un changement de nom d’une classe CSS ? Ces changements sont dans tous les cas volontaires et le fait de couvrir le code de cette façon ne vous sauvera pas d’une erreur. Et puis, même si ce cas arrivait, un petit update du snapshot et on est tranquille (qui n’a jamais fait ça ?).

Les snapshots génèrent également pléthore de fichiers lourds et inutiles qu’il est obligatoire d’ajouter à vos commits. Cette accumulation de fichiers quasi-illisibles rend très pénible la lecture de pull-requests.

Tester un composant

Mais alors comment remplacer ces snapshots ? Prenons un exemple de code d’un composant React :

L’affichage de ce composant est conditionné en fonction de sa propriété status. Si nous souhaitons tester tous les cas de figure en utilisant des snapshots, il faudra en utiliser autant qu’il y a de valeurs possibles à la propriété status, soit trois dans notre cas. Sans snapshot, voici à quoi ressemblerait le code des tests unitaires :

Un test correspondant à chaque cas de figure du component , il n’est pas utile de tester l’ensemble du template, nous devons juste vérifier qu’il soit bien rendu et tester les différents cas de figures. Nous devons vérifier la présence d’un élément, mais également vérifier qu’il soit bien absent dans l’autre cas. Pour ça, nous utilisons .find() de Enzyme avec soit des sélecteurs CSS soit des Composants React.

Attention, lorsque votre composant est appelé dans shallow(), Jest considère que l’intégralité de son template est testé (hormis les fonctions). Pour rendre ce test pertinent, à vous de vérifier la présence ou non d’éléments en fonction de leur importance.

Avec juste quelques tests faciles à écrire et à comprendre nous obtenons rapidement un coverage suffisant. Mais dans notre cas nous n’obtenons pas 100 % de couverture. Que nous manque-t-il ?

Jest nous indique que les lignes 12 et 16 ne sont pas couvertes, et c’est normal : Jest voit là des fonctions dont l’exécution n’a pas été testée. Pour le cas de la ligne 16, c’est très simple, il nous suffit de simplifier notre code. En effet, le code () => onClick() peut s’écrire simplement : onClick. Cela ne changera strictement rien au comportement, mais Jest ne le voit plus comme une fonction, cette ligne est désormais couverte (la fonction onClick sera testée dans le container).

En ce qui concerne la ligne 12, nous ne pouvons pas simplifier le code, cela changerait son comportement. Pour vérifier que notre fonction onChange soit bien appelée, nous utiliserons la fonction shallow() de Enzyme pour ensuite, simuler un changement sur notre input. Il suffit de passer en props une fonction mockée (selectItem) et vérifier qu’elle a bien été appelée avec le bon paramètre (“name”).

Tester un container

Depuis l’avènement des Hooks de React, tester un container est devenu plus complexe. Parfois mal compris ou sur-utilisés, ils génèrent souvent du code difficile à tester. Les librairies de tests disponibles n’aident pas vraiment tant elles sont peu intuitives et difficiles à prendre en main. Pourtant, obtenir 100 % de couverture de code sur un container n’est pas si difficile ou contraignant. Voyons comment s’y prendre sur le container suivant :

Avant même de rédiger le moindre test on remarque un problème récurrent dans les container React utilisant les Hooks, une fonction beaucoup trop grosse. Une bonne pratique est déjà de diviser cette fonction en plusieurs fonctions. Le code résultant sera plus lisible, potentiellement réutilisable et bien sûr plus facilement testable. Un autre avantage est qu’en divisant votre code de la sorte, vous ne serez plus embêté par ESLint et le tableau de dépendances d’un hook (fini le commentaire “eslint-disable-next-line react-hooks/exhaustive-deps” 🎉).

Toutes nos fonctions utilisées dans les Hooks (et les autres) sont maintenant facilement testables de manière unitaire. Nous pouvons leur passer en paramètre ce que l’on souhaite comme valeur, simuler des dépendances et passer des fonctions mockées.

Il ne reste maintenant qu’une seule fonction à tester : useHooks. Cette fonction ne fait qu’exécuter des fonctions de React, nous n’avons aucun intérêt à la tester en détail. Le bon fonctionnement de useState ou useCallback dépend de la responsabilité de React. En revanche, il est de notre responsabilité de s’assurer que notre fonction useHook retourne bien notre composant. React empêche de tester unitairement cette fonction, pas de problème, nous pouvons la shallow comme un simple composant et vérifier qu’elle contient bien notre composant. Si tel est le cas, alors notre container fait bien son travail.

Si nous vérifions maintenant notre couverture de code, nous voyons bien apparaître 100 % pour notre composant et son container, et tout ça sans trop de difficulté et sans trop de dépendance.

Un test de coverage nous confirme bien que nous obtenons 100 % de couverture de code.

Si vous souhaitez jouer avec le code servant d’exemple à cet article, il est disponible sur Github.

👉 Auteur : Jérôme BOUKORRAS

🔎 Lire les autres articles de Jérôme

--

--