Utilisez les tests automatisés dans React
Peu importe le langage dans lequel vous développez, les tests font partie intégrante du métier de développeur, même en frontend. Ils permettent de s'assurer de la fiabilité de votre code.
Comprenez l'utilité des tests
Rédiger des tests prend du temps et de la réflexion. Et pourtant, vous pouvez vous considérer comme chanceux : depuis quelques années apparaissent des outils de plus en plus simples à utiliser pour tester le JS. Encore une fois, quand vous codez seul sur une petite application, ça peut vite vous sembler pénible pour pas grand-chose. Mais essayez de vous projeter. ✨
Lorsque vous travaillez sur une base de code qui comporte de nombreuses fonctionnalités, et que vous codez à plusieurs, il est si simple de faire une modification qui amène une régression (introduction d'un bug en production). Surtout lorsque vous touchez à du code que vous n'avez pas écrit vous-même... et donc pour lequel vous ne saisissez pas toujours toutes les subtilités. Dans ces cas-là, c'est un vrai atout de savoir que vous pouvez compter sur les tests pour vous signaler une erreur ! Vous évitez les régressions, et vous gagnez en confiance sur vos modifications.
Mais "test" est un tout petit mot qui recouvre une réalité bien plus grande : il existe de nombreux types de tests.
Faites la différence entre les types de tests
Il existe différents types de tests, les principaux sont les tests unitaires, d'intégration et end-to-end.
Cela ne veut pas dire que vous devez totalement abandonner les tests unitaires et les tests end-to-end, mais qu'il est important de trouver un juste milieu entre les trois types. Par exemple, vous pouvez totalement choisir d'implémenter des tests end-to-end spécifiquement pour une fonctionnalité "critique" de votre application.
Alors, mettons-nous au travail dès maintenant ! 👩💻
Créez votre premier test avec Jest
Comme tout ce qui a trait à JavaScript, l'écosystème des tests évolue très vite. Dans cette partie, nous allons utiliser Jest et React Testing Library.
Pour sa part, Jest fait partie des outils acclamés depuis plusieurs années, et il se trouve également que Jest est déjà installé avec Create-React-App. Pas mal pour se lancer dans les tests. 🚀
Quant à React Testing Library, il s'agit d'une bibliothèque qui donne accès à davantage d'outils permettant de tester des composants. Nous la découvrirons dans les deux prochains chapitres.
Si vous vous demandez comment les deux s'articulent, vous pouvez vous dire que Jest est l'outil de base pour vos tests, et que React Testing Library est l'outil qui vous facilite les tests de composants.
Plongeons ensemble dans le monde des tests avec un premier exemple de Jest :
Maintenant, écrivons un test unitaire.
Préparez votre code
Pour tester notre code de manière indépendante, nous allons sortir une partie de notre logique sur la page /Results/index.jsx
. On peut faire la fonction :
export function formatJobList(title, listLength, index) {
if (index === listLength - 1) {
return title
}
return `${title},`
}
Et dans notre JSX, on met :
theme={theme}
Les compétences dont vous avez besoin :
{resultsData &&
resultsData.map((result, index) => (
key={`result-title-${index}-${result.title}`}
theme={theme}
{formatJobList(result.title, resultsData.length, index)}
))}
Nous voilà donc fin prêts pour notre test.
Créez votre fichier de test
Depuis le début du cours, tous nos fichiers sont répartis dans des dossiers ayant un nom spécifique et un fichier index.jsx
. Eh bien, cette répartition va nous être bien utile car elle va nous permettre de mettre nos tests directement à la racine de chaque dossier.
On va donc commencer par /Results
et y créer un fichier index.test.js
, et voilà !
Mais comment Jest va retrouver notre fichier de test ?
Eh bien, pas de panique. Jest est ici configuré pour chercher dans tous les sous-dossiers (à part node_modules
et .git
, notamment) à la recherche de fichiers se terminant par spec.js
ou test.js
, précédé d’un trait d’union ( - ) ou d’un point ( . ). C'est également possible de mettre vos tests dans un dossier __tests__
.
Comprenez la rédaction du test
Attelons-nous maintenant à la rédaction du test.
Il nous faut dans un premier temps importer l'élément à tester, puis utiliser test
.
On utilise test
, mais on ne l'a importé nulle part ? Pourquoi on n'a pas une erreur ici ?
Eh bien, test est un outil auquel on peut accéder globalement dans un fichier de test grâce à Jest. Il existe d'autres outils globaux, vous pourrez en apprendre davantage sur la documentation (en anglais).
Vérifions dès maintenant que notre test fonctionne. Dans Results/results.test.js
, on importe notre fonction, et on prépare le test :
import { formatJobList } from './'
test('Ceci est mon premier test', () => {})
Notez bien que test()
prend une string
en premier argument
, puis une fonction en deuxième argument.
J'essaie dès maintenant de lancer la commande yarn run test
dans mon terminal.
… C'est complètement normal. Ici, nous n'avons pas écrit le cœur de notre test : exécuter notre fonction, et comparer avec une référence.
Pour cela, on va utiliserexpect
ettoEqual
. Ici, toEqual
est ce qui s'appelle un matcher, mis à disposition par Jest. On utilise la fonction expect()
, qui va comparer un élément avec notre matcher. Cela nous oblige à nous interroger sur ce qu'on veut obtenir de formatJobList
.
On prend par exemple un élément item2
qui sera en deuxième position dans notre liste (son index est donc de 1), mais qui ne sera pas le dernier élément : on veut donc que le titre ajoute une virgule.
Ce qui nous donne :
import { formatJobList } from './'
test('Ceci est mon premier test', () => {
const expectedState = 'item2,'
expect(formatJobList('item2', 3, 1)).toEqual(expectedState)
})
On sauvegarde, et nos tests se lancent automatiquement (sauf si on a quitté le mode watch
). On a bien du vert : yay ! 🎉
Vous avez vu : ce n'était pas si dur, n'est-ce pas ?
Il existe aussi d'autres fonctions, telles que describe()
.
describe
vous permet d'englober plusieurs tests qui ont un lien entre eux (vous êtes libre de choisir quel est ce lien), et que cela s'affiche de manière plus lisible lorsque vous lancez vos tests. Dans notre exemple, on peut maintenant ajouter un test pour vérifier que notre fonction ne met pas de virgule sur le dernier élément :
import { formatJobList } from './'
describe('La fonction formatJobList', () => {
test('ajoute une virgule à un item', () => {
const expectedState = 'item2,'
expect(formatJobList('item2', 3, 1)).toEqual(expectedState)
})
test('ne met pas de virgule pour le dernier élément', () => {
const expectedState = 'item3'
expect(formatJobList('item3', 3, 2)).toEqual(expectedState)
})
})
Ce qui nous donne :
C'est beaucoup plus lisible, n'est-ce pas ? 👀
Comme pour tout, il existe des conventions de rédaction de tests pour que les appellations soient les plus explicites possibles. Une des conventions possibles consiste à commencer tous les tests par "should". Dans ce cas, c'est encore plus explicite d'utiliser l'alias it
dont je viens de vous parler. Ce qui aurait donné dans notre cas :
import { formatJobList } from './'
describe('The formatJobList function', () => {
it('should add a comma to a word', () => {
const expectedState = 'item2,'
expect(formatJobList('item2', 3, 1)).toEqual(expectedState)
})
it('should not add a comma to the last element of the list', () => {
const expectedState = 'item3'
expect(formatJobList('item3', 3, 2)).toEqual(expectedState)
})
})
Assurez-vous d'avoir le test coverage idéal
Lorsqu’on commence à avoir des tests, il devient possible de mesurer la couverture de tests (code coverage), c’est-à-dire le pourcentage de notre code – à l’expression près ! – qui est couvert par les tests. On peut alors repérer les parties non testées, ou insuffisamment testées, et savoir ainsi où concentrer nos prochains efforts d’écriture de tests.
Lançons dès maintenant la commande nous permettant de vérifier notre code coverage.
Pour cela, je fais yarn test -- --coverage
.
On a donc le détail de la couverture de nos tests, y compris des lignes qui ne sont pas couvertes par les tests.
Il existe des services qui permettent d’utiliser la couverture de tests comme critère de blocage pour l’intégration de nouveau code à nos projets, en définissant des exigences de taux absolu plancher, ou l’interdiction de faire baisser le taux existant, pour autoriser une pull request à être fusionnée dans sa branche destinataire.
Il peut être très satisfaisant d'augmenter son code coverage au maximum. Mais attention, le code coverage peut être traître : non seulement, vous pouvez perdre trop de temps afin d'essayer d'obtenir 100 % de couverture, ce qui, la plupart du temps, n'est pas nécessaire. D'autant plus quand on sait que les tests doivent être maintenus dans le temps, en gardant la même logique à l'esprit. Et un autre point de vigilance : le coverage ne prend pas du tout en compte la pertinence de vos tests. Alors, ne vous laissez pas aveuglément séduire ! 🙈
Exercez-vous
Pour cet exercice, vous continuerez à tester pages/results/index.jsx
avec la fonction formatQueryParams
. Comme d'habitude, vous trouverez la base du code nécessaire à l'exercice sur la branche P3C1-begin
.
Vous devrez créer deux tests différents pour formatQueryParams
au sein d'une série de tests (regroupés dans describe()
).
Les solutions se trouvent sur la branche P3C1-solution
.
En résumé
Les tests permettent d'être sûr de soi lorsqu'on veut modifier une codebase, surtout lorsqu'ils sont intégrés dans des pratiques de déploiement continu.
Les principaux types de tests sont les tests unitaires, les tests d'intégration et les tests end-to-end.
Les tests d'intégration sont un bon compromis entre le temps passé à rédiger les tests et la sécurité garantie.
Jest et React Testing Library mettent à la disposition des développeurs un ensemble d'outils et fonctions permettant de tester une application.
Bon, ça peut faire peur, les tests, mais au final, ça va ? Rien d'insurmontable jusque là ? Ça tombe bien, on continue notre exploration des tests dans le prochain chapitre, où on va apprendre à utiliser React Testing Library pour tester nos composants. 💣
Alors à tout de suite !