NodeJS: quelques trucs et astuces pour vos tests avec Jest

Remi Jacquart
Just-Tech-IT

--

Introduction

Il y a quelques semaines, j’ai réalisé une migration des tests unitaires et des scénarios Gherkin conçus à partir des librairies de tests Mocha, Sinon, Chai vers le framework de tests Jest.

Je connaissais déjà Jest pour l’avoir pratiqué à plus petite échelle par le passé. Ce travail de migration fut de longue haleine mais cela m’a permis de développer mes compétences et connaissances autour de cette librairie.

Je souhaite donc faire part de quelques trucs et astuces basés sur des exemples concrets pour aider à la prise en main de Jest.

Jest présente de nombreux avantages :

  • permet de se configurer de manière simple
  • permet d’exécuter les tests en parallèle et de manière isolée (les tests s’exécutent ainsi plus rapidement)
  • possède une communauté d’utilisateurs et de contributeurs très importante
  • compatible avec la syntaxe Jasmine (la migration est ainsi facilitée)

Un projet d’exemple

J’ai conçu un petit projet NodeJS autour du championnat de basket de la NBA afin d’exposer différentes situations courantes lorsque l’on cherche à tester une application NodeJS (voir ici : https://github.com/remija/jest-tricks) :

- comment “mocker” en partie un module ?
- comment “mocker” un module externe ?
- comment “mocker” des méthodes asynchrones avec promesses ?
- comment “mocker” des méthodes asynchrones avec callback ?
- comment “mocker” des Streams en lecture ?
- comment “mocker” des Streams en écriture ?
- comment “mocker” un EventEmitter ?

Le module championship expose une classe ES6 Championship ainsi qu’un objet Championship NBA.

Le module team expose une classe ES6 Team permettant de modéliser une équipe sportive.
Le module nba-team expose une classe ES6 étendant Team, NBATeam.
Le module nba-service expose des services permettant de récupérer des données concernant les équipes et joueurs NBA.
Le module game-notifier expose une classe GameNotifier étendant un EventEmitter, permettant de se renseigner sur les résultats NBA.

J’ai écrit plusieurs tests unitaires autour des🍀 Boston Celtics 🍀pour le module nba-team qui permettent de découvrir comment tester tous les cas de figure cités plus haut.

Allez, on regarde tout ça !

Comment “mocker” en partie un module ?

NBATeam requiert l’objet Championship NBA du module championship.

On souhaite tester la méthode register qui fait appel à la méthode registerTeam de NBA.
Pour cela nous allons devoir “mocker” le championnat NBA et modifier le comportement de la méthode registerTeam suivant les cas de figure.
Par contre, on ne souhaite pas “mocker” tout le module championship.

Pour cela, Jest propose la méthode requireActual qui permet de récupérer l’implémentation originale du module. Ensuite on peut surcharger ce que l’on a réellement besoin de “mocker”.

Comment “mocker” des méthodes asynchrones avec promesses ?

La méthode registerTeam du championnat NBA est asynchrone. Il nous faut donc redéfinir le comportement de celle-ci dans tous les cas à couvrir.

Pour cela, on va notamment s’appuyer sur les méthodes mockResolvedValue et mockRejectedValue qui permettent de simuler le résultat de méthodes asynchrones à base de promesses Javascript natives.

Attention, il ne faut pas utiliser mockResolvedValue et mockRejectedValue raccourcies si vos méthodes se basent sur une librairie de promesses type Bluebird au risque d’avoir des comportements non attendus.

Comment “mocker” un module externe ?

NBATeam requiert certaines méthodes du module nba-service. Ce service effectue des appels à une API exposée sur Internet (voir l’API NBA: https://rapidapi.com/api-sports/api/api-nba).

On utilise la librairie request pour effectuer les appels REST vers cette API.

Nous allons donc devoir “mocker” les appels pour couvrir tous les cas de figure.

Le nommage des objets pour “mocker” un module est important. En effet il faut absolument les préfixer par “mock”.
Dans le cas contraire, Jest provoque une erreur. C’est une précaution contre les variables de “mock” non initialisées.

Comment “mocker” des méthodes asynchrones avec callback ?

La méthode request permet de faire des appels HTTP asynchrones. On récupère le résultat grâce à une méthode “callback”.

Nous allons tester la méthode buildPlayersByTeamId de NbaTeam qui fait elle-même appel à la méthode getPlayersByTeamId de NbaService qui utilise request pour charger les données.

La méthode request prend deux paramètres, dont la méthode “callback”. Il suffit de surcharger request grâce à mockImplementation et faire en sorte d’appeler le callback (une méthode avec trois paramètres) avec les bons
paramètres suivant les situations testées.

Comment “mocker” des Streams en lecture ?

NbaTeam propose une méthode importHistoricPlayers pour importer une liste de joueurs historiques dans l’équipe à partir d’un fichier JSON. On utilise pour cela la méthode createReadStream du module fs.

On commence par “mocker” la méthode createReadStream du module fs.

On simule la lecture d’un fichier JSON en créant un “stream” de lecture simple dans lequel on insère plusieurs “chunks” de données.

On simule une erreur dans la lecture d’un fichier JSON en émettant une erreur (les Streams héritent d’un EventEmitter).

Comment “mocker” des Streams en écriture ?

NbaTeam propose une méthode exportPlayers pour exporter la liste des joueurs de l’équipe dans un fichier JSON. On utilise pour cela la méthode createWriteStream du module fs.

Comme vu précédemment, on avait “mocké” la méthode createWriteStream du module fs.

On simule l’écriture d’un fichier JSON en créant un “stream” d’écriture simple dans lequel on va concaténer le contenu à écrire. L’appel à la méthode de callback permet d’indiquer que le “chunk” a été traité. Dans le cas d’une erreur, on appelle la “callback” avec l’erreur en paramètre.

Comment “mocker” un EventEmitter ?

NbaTeam propose une méthode checkLatestResult qui permet de récupérer le dernier résultat de l’équipe et d’incrémenter les compteurs de victoires et de défaites.

Pour cela, un GameNotifier émet des événements qui sont ensuite traités par NbaTeam.

On commence par “mocker” le module game-notifier. On déclare le GameNotifier “mocké” comme EventEmitter pour profiter du prototype. On va ensuite “mocker” avec Jest les méthodes supplémentaires qui le nécessite.

La méthode checkLatestResult est asynchrone. Il faut donc faire en sorte d’émettre les événements à tester avant de résoudre la promesse.

Nous avons vu comment tester différentes situations avec le framework Jest !

J’espère que cela vous aidera dans l’écriture de vos tests avec Jest ;)

Vous trouverez le code et les tests de mon projet ici : https://github.com/remija/jest-tricks

--

--

Remi Jacquart
Just-Tech-IT

Développeur NodeJS et ReactJS à la DSI AXA France