Passer au contenu principal

Conçu pour fonctionner aussi bien sur du JavaScript côté navigateur que serveur, Jest a su convaincre par sa rapidité d’exécution des tests, son API complète et sa facilité d’installation

Créée et maintenu par Facebook en 2014, Jest, rendu open-source, est une librairie de test JavaScript ayant énormément gagné en popularité depuis sa mise en libre circulation. Conçu pour fonctionner aussi bien sur du JavaScript côté navigateur (frontend) que côté serveur (backend), Jest a su convaincre par sa rapidité d’exécution des tests, son API complète et sa facilité d’installation. Sa documentation complète et bien maintenue en fait la librairie la plus populaire pour tous les différents tests js.

Jest est sans conteste l’un des outils nodejs les plus incontournables pour un développeur Javascript.

Pourquoi Jest ?

Les tests javascript rapides

En 2017, Airbnb a migré tous ses tests de Mocha vers Jest pour les gains de performances en termes de temps d’exécution. D’après un article Medium signé Gary Borton, ingénieur chez Airbnb, ce changement a réduit le temps d’exécution de leur suite de tests de plus de 12 minutes à 4 minutes 30.

Un des principes fondamentaux des tests est qu’ils doivent être rapides. En effet, la valeur des tests réside dans le fait que les développeurs vont se baser dessus à chaque développement pour pouvoir construire et refactorer leur code sans avoir peur de causer une régression. Ils doivent donc pouvoir les lancer aussi souvent qu’il le faut sans avoir l’impression que le temps d’exécution des suites de tests ralentit leur travail.

Pour pouvoir atteindre ces performances en termes de temps de traitement, Jest lance plusieurs suites de tests en parallèle dans un pool de child_process nodejs. Cette exécution en parallèle va pouvoir produire sa pleine puissance lorsqu’elle est exécutée sur une machine ayant suffisamment de ressources CPU et RAM pour lancer efficacement ces tests simultanément. Pour que vos tests fonctionnent bien, il est d’autant plus important avec Jest que ces derniers soient isolés les uns des autres. Il s’agit là aussi d’un fondamental d’un test bien conçu.

En effet, si un test influe sur un autre test, il se peut que vous ne vous en rendiez pas compte si les tests sont lancés dans l’ordre. Sauf qu’avec Jest ils seront lancés en simultané et si l’ordre est inversé, votre test échouera et ça ne sera pas dû à une erreur dans le code.

“L’homme” à tout faire

Contrairement à Mocha où il faut le plus souvent ajouter toute une suite de librairies pour avoir l’ensemble des outils nécessaires à une rédaction de tests, Jest est fourni “toutes options”.Là où en utilisant Mocha il aurait fallu ajouter Sinon pour réaliser les spies, stubs et mock, puis ajouter Istanbul pour avoir le code coverage, Jest est livré avec ces fonctionnalités.

Installer Jest sur un projet Nodejs

Jest brille aussi par sa simplicité d’installation, de configuration et d’utilisation. En une commande NPM ou Yarn, vous êtes prêts à écrire votre premier test JavaScript.

Install avec NPM

Pour installer Jest, il suffit d’entrer la commande suivante dans votre terminal, au niveau de votre répertoire de travail:

npm install jest --save-dev

ou son raccourci:

npm i -D jest
Installer Jest avec NPM pour nodejs

Installez Jest avec la commande npm i -D jest

L’option save-dev ou -D ordonne à npm de n’installer ce package que si l’environnement, défini par la variable d’environnement NODE_ENV, n’est pas celui de production. En effet, inutile de surcharger son serveur de production de dépendances de tests puisque vous n’aurez pas vocation à faire tourner ceux-ci en production. Vous lancerez les tests en local ou sur votre pipeline d’intégration continue.

Lancer Jest

Une fois Jest installé comme dépendance sur votre répertoire de travail, vous pouvez lancer les tests en tapant la commande jest sur votre terminal.

Lancez vos tests avec la commande Jest dans votre terminal

Lancez vos tests avec la commande Jest dans votre terminal

Avec cette commande, Jest va chercher dans votre répertoire de travail, à l’exception du répertoire node_modules, les fichiers présents dans un répertoire __tests__ ou ayant un nom de fichier se terminant par .spec.js ou .test.js (ou .ts si vous faites du Typescript).

Bien évidemment, n’ayant écrit encore aucun test, Jest ne peut pas fonctionner. Allons maintenant écrire notre premier test. J’ai créé la fonction très triviale ci-dessous que nous allons tester:

const theTruth = () => truemodule.exports = { theTruth }

Nous voulons tester que lorsqu’on appelle la fonction theTruth, celle-ci nous retourne toujours true. Pour faire ça, nous allons créer un fichier nommé theTruth.spec.js dans lequel je vais écrire le squelette d’un test.

describe('ma première suite de tests', () => {  test('mon premier test', () => {    expect(false).toBe(true)  })})

Ce test ne passera jamais car je lui demande de s’attendre à ce que false soit true Mais je commence par un exemple pour m’assurer que Jest fonctionne bien et vous expliquer la structure d’une suite de tests sur Jest.

La fonction describe de Jest permet d’englober une série de tests. C’est ce qu’on appelle une suite de tests. Vous pouvez avoir autant de suites que vous voulez, dans un même fichier ou dans plusieurs fichiers. Elle sert de chapitre et vous permet de vous y retrouver quand vous lirez le rapport des tests une fois que vous aurez lancé Jest.
La fonction test, que vous pouvez aussi utiliser via son alias it, va englober le test en lui-même. C’est là que vous allez mettre en place les variables nécessaires puis donner le résultat attendu. Dans le expect doit se trouver la variable, et dans le .toBe le résultat attendu.

exemple de test jest echec
Avec la commande Jest, je peux lancer ce test voué à l’échec

En changeant le test pour mettre expect(false).toBe(false), cela va nous donner le résultat suivant :

Illustration d'un test jest qui passe
Corriger ce test m’a permis de le voir réussir, bien qu’il ne teste rien

Nous savons donc que Jest est bien fonctionnel. Place au vrai test maintenant. Il faut importer la fonction que nous souhaitons tester dans notre fichier de test, la placer dans une variable à l’intérieur de notre test, et modifier la fonction expect pour qu’elle évalue cette variable.

const {theTruth} = require('./index')describe('ma première suite de tests', () => {  test('mon premier test', () => {    const isTrue = theTruth()    expect(isTrue).toBe(true)  })})

Le fait d’assigner le résultat de ma fonction dans une variable me permet d’avoir un test plus lisible. Si maintenant je lance Jest :

Ma fonction theTruth retourne bien true, mon test passe
Ma fonction theTruth retourne bien true, mon test passe

J’ai mon code qui est fonctionnel et mon test unitaire qui a réussi.

Grâce à Jest vous pouvez également évaluer votre couverture de code en passant l’option -- coverage:

Notre couverture de test est à 100% car notre test unitaire passe bien par toutes les lignes de code du fichier index.js
Notre couverture de test est à 100% car notre test unitaire passe bien par toutes les lignes de code du fichier index.js

Dans ce cas très simple, Jest me confirme qu’il est bien passé par 100% des cas de mon code. Si vous aviez plusieurs “branches”, notamment avec des conditions if/else, il faudrait écrire un test pour chaque cas d’if afin d’avoir une couverture de code complète.

Assertions dans Jest

Les assertions Jest les plus communes

Pour vérifier que la fonction que vous êtes en train de tester retourne bien ce que vous souhaitez, il faut utiliser une des assertions d’égalité.

L’assertion .toBe est la plus large. Vous pouvez vérifier que la variable attendue soit une instance de Number ou qu’elle soit true directement. Mais il existe de nombreuses assertions plus spécifiques comme .toBeTruthy, .toBeInstanceOf, .toBeLessThanOrEqual qui vous permettent d’avoir des tests plus explicites.

Vous avez également des assertions liées aux appels de vos fonctions, que vous devrez utiliser avec un Mock. Jest nomme les stubs et les mocks de la même façon, bien qu’il y ait une nuance. Il s’agit de fausses fonctions, qui viennent remplacer le comportement d’une vraie fonction présente dans votre code et qui permettent de simuler un comportement. Ces mocks sont essentiels dans la conception des tests unitaires.

Dans le cas des mocks, vous pourrez appeler des assertions telles que .hasBeenCalledWith ou .hasBeenCalledTimes pour vous assurer qu’au long de l’exécution de votre code, vous avez bien utilisé votre fonction le bon nombre de fois avec les bons paramètres.

Le cas des spy, stubs et mocks

Jest vous propose la possibilité de remplacer vos fonctions par des “jest functions”. Ces dernières se substituent aux véritables fonctions de votre code et retourneront ce que vous voudrez.

Prenons l’exemple d’une fonction getCharacter, qui elle-même appelle une autre fonction getBulbosaur. Cette dernière a pour but d’aller chercher les propriétés de ce Pokémon sur une API externe.

la fonction getBulbosaur fait un appel vers une API externe, chose qu'on ne doit pas tester
la fonction getBulbosaur fait un appel vers une API externe, chose qu’on ne doit pas tester

Pour que nos tests soient bien écrits, il faut abstraire l’appel à l’API. Dans le cas d’un test unitaire, on va créer un stub (un mock dans le cas de Jest) qui va me retourner un objet contenant le Pokémon et ses propriétés.

Je crée l'objet getElements afin de pouvoir "mocker" la méthode getBulbosaur
Je crée l'objet getElements afin de pouvoir "mocker" la méthode getBulbosaur
Je crée l’objet getElements afin de pouvoir « mocker » la méthode getBulbosaur

Dans un premier temps je crée un objet getElements qui englobe chacune des deux fonctions. Ensuite, je change l’appel de getBulbosaur() dans getCharacter pour qu’elle fasse référence à l’objet getElements plutôt que directement. Cette modification n’apporte aucune conséquence à votre code mais est essentielle pour pouvoir mocker la fonction getBulbosaur().

Dans mon test, j'utilise la méthode jest.fn et mockReturnValue pour abstraire l'appel API
Dans mon test, j’utilise la méthode jest.fn et mockReturnValue pour abstraire l’appel API

Je modifie ensuite mon test pour inclure une “Jest function” à la place de la véritable fonction getBulbosaur. Bien que je n’invoque jamais cette fonction directement dans mon test, elle est invoquée à l’intérieur de ma fonction getCharacter.

Dans ce test, je cherche à m’assurer qu’en appelant la fonction getCharacter, je passe bien par la fonction getBulbosaur qui appellera l’API. J’évalue donc que la fonction getBulbosaur a bien été appelée. Inutile cependant de vérifier que le retour est bien “Bulbosaur” parce que c’est défini “en dur” dans le mockReturnValue.

Comment bien rédiger ses tests nodejs

Savoir installer Jest et écrire un test est la fondation à l’édifice d’un meilleur code. Cette compétence essentielle va néanmoins demander de la pratique afin de savoir rédiger correctement des tests unitaires et des tests fonctionnels pour assurer la stabilité de votre codebase.

Savoir rédiger des tests sert aussi à pouvoir appliquer la méthodologie Test Driven Development (TDD). Grâce à elle, vous pourrez coder plus efficacement et découvrir vos algorithmes étape par étape, en rédigeant d’abord des tests unitaires représentant les résultats souhaités avant d’en écrire le code.

Rayed Benbrahim

Auteur Rayed Benbrahim

Plus d'articles par Rayed Benbrahim

Laisser un commentaire