• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 23/02/2024

Découvrez la base des tests dans React avec Jest

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 :

<ResultsTitle theme={theme}>
    Les compétences dont vous avez besoin :
    {resultsData &&
        resultsData.map((result, index) => (
            <JobTitle
                key={`result-title-${index}-${result.title}`}
                theme={theme}
            >
                {formatJobList(result.title, resultsData.length, index)}
            </JobTitle>

    ))}
</ResultsTitle>

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.

Jest ne trouve aucun test quand on essaie de lancer yarn run test
Aucun fichier de test n'est trouvé

… 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 utiliserexpectettoEqual . 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 ! 🎉

Notre premier test est tout bon !
Notre premier test est tout bon !

Vous avez vu : ce n'était pas si dur, n'est-ce pas ?

Il existe aussi d'autres fonctions, telles que describe() . 

describevous 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 :

Les deux tests fonctionnent bien dans la console
Nos deux tests marchent !

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  .

Le test coverage s'affiche sous la forme d'un tableau dans le terminal
Le test coverage s'affiche sous la forme d'un tableau dans le terminal

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 !

Exemple de certificat de réussite
Exemple de certificat de réussite