Tester JavaScript avec Jest

Jest est une bibliothèque pour tester le code JavaScript. C'est un projet open source maintenu par Facebook, et il est particulièrement bien adapté pour les tests de code React, bien que non limité à cela: il peut tester n'importe quel code JavaScript. Jest est très rapide et facile à utiliser

Introduction à Jest

Jest est une bibliothèque pour tester le code JavaScript.

C'est un projet open source maintenu par Facebook, et il est particulièrement bien adapté pour les tests de code React, bien que non limité à cela: il peut tester n'importe quel code JavaScript. Ses atouts sont:

  • c'est rapide
  • il peut effectuertest d'instantané
  • il est avisé et fournit tout ce qui est prêt à l'emploi sans vous obliger à faire des choix

Jest est un outil très similaire à Mocha, bien qu'il présente des différences:

  • Mocha est moins opiniâtre, tandis que Jest a un certain ensemble de conventions
  • Mocha nécessite plus de configuration, tandis que Jest fonctionne généralement hors de la boîte, grâce à son opinion
  • Mocha est plus ancien et plus établi, avec plus d'intégrations d'outillage

À mon avis, la plus grande caractéristique de Jest est qu'il s'agit d'une solution prête à l'emploi qui fonctionne sans avoir à interagir avec d'autres bibliothèques de test pour effectuer son travail.

Installation

Jest est automatiquement installé danscreate-react-app, donc si vous l'utilisez, vous n'avez pas besoin d'installer Jest.

Jest peut être installé dans n'importe quel autre projet en utilisantFil:

yarn add --dev jest

ounpm:

npm install --save-dev jest

remarquez comment nous demandons à tous les deux de mettre Jest dans ledevDependenciespartie de lapackage.jsonfichier, de sorte qu'il ne sera installé que dans l'environnement de développement et non en production.

Ajoutez cette ligne aux scripts de votrepackage.jsondéposer:

{
  "scripts": {
    "test": "jest"
  }
}

afin que les tests puissent être exécutés en utilisantyarn testounpm run test.

Vous pouvez également installer Jest globalement:

yarn global add jest

et exécutez tous vos tests en utilisant lejestoutil de ligne de commande.

Créez le premier test Jest

Projets créés aveccreate-react-appavoir Jest installé et préconfiguré prêt à l'emploi, mais l'ajout de Jest à n'importe quel projet est aussi simple que de taper

yarn add --dev jest

Ajoutez à votrepackage.jsoncette ligne:

{
  "scripts": {
    "test": "jest"
  }
}

et lancez vos tests en exécutantyarn testdans votre coquille.

Maintenant, vous n'avez aucun test ici, donc rien ne sera exécuté:

Testing with Yarn

Créons le premier test. Ouvrir unmath.jsfichier et tapez quelques fonctions que nous testerons plus tard:

const sum = (a, b) => a + b
const mul = (a, b) => a * b
const sub = (a, b) => a - b
const div = (a, b) => a / b

module.exports = { sum, mul, sub, div }

Créez maintenant unmath.test.jsfichier, dans le même dossier, et là, nous utiliserons Jest pour tester les fonctions définies dansmath.js:

const { sum, mul, sub, div } = require('./math')

test(‘Adding 1 + 1 equals 2’, () => { expect(sum(1, 1)).toBe(2) }) test(‘Multiplying 1 * 1 equals 1’, () => { expect(mul(1, 1)).toBe(1) }) test(‘Subtracting 1 - 1 equals 0’, () => { expect(sub(1, 1)).toBe(0) }) test(‘Dividing 1 / 1 equals 1’, () => { expect(div(1, 1)).toBe(1) })

Fonctionnementyarn testentraîne l'exécution de Jest sur tous les fichiers de test qu'il trouve et nous renvoie le résultat final:

Passing tests

Exécuter Jest avec VS Code

Visual Studio Code est un excellent éditeur pour le développement JavaScript. LeExtension de plaisanterieoffre une intégration de premier ordre pour nos tests.

Une fois que vous l'avez installé, il détectera automatiquement si vous avez installé Jest dans vos devDependencies et exécutera les tests. Vous pouvez également appeler les tests manuellement en sélectionnant leBlague: Start Runnercommander. Il exécutera les tests et restera en mode veille pour les réexécuter chaque fois que vous modifiez l'un des fichiers qui ont un test (ou un fichier de test):

A simple Jest test running in VS Code

Matchers

Dans l'article précédent, j'ai utilisétoBe()comme le seulmatcher:

test('Adding 1 + 1 equals 2', () => {
  expect(sum(1, 1)).toBe(2)
})

Un matcher est une méthode qui vous permet de tester des valeurs.

Matchers les plus couramment utilisés, comparant la valeur du résultat deexpect()avec la valeur passée en argument, sont:

  • toBecompare l'égalité stricte, en utilisant===
  • toEqualcompare les valeurs de deux variables. S'il s'agit d'un objet ou d'un tableau, il vérifie l'égalité de toutes les propriétés ou éléments
  • toBeNullest vrai lors du passage d'une valeur nulle
  • toBeDefinedest vrai lors du passage d'une valeur définie (à l'opposé de ce qui précède)
  • toBeUndefinedest vrai lors du passage d'une valeur indéfinie
  • toBeCloseToest utilisé pour comparer des valeurs flottantes, évitant les erreurs d'arrondi
  • toBeTruthytrue si la valeur est considérée comme vraie (comme unifEst-ce que)
  • toBeFalsytrue si la valeur est considérée comme false (comme unifEst-ce que)
  • toBeGreaterThantrue si le résultat de expect () est supérieur à l'argument
  • toBeGreaterThanOrEqualtrue si le résultat de expect () est égal à l'argument, ou supérieur à l'argument
  • toBeLessThantrue si le résultat de expect () est inférieur à l'argument
  • toBeLessThanOrEqualtrue si le résultat de expect () est égal à l'argument, ou inférieur à l'argument
  • toMatchest utilisé pour comparer des chaînes avecexpression régulièrecorrespondance de modèle
  • toContainest utilisé dans les tableaux, true si le tableau attendu contient l'argument dans son ensemble d'éléments
  • toHaveLength(number): vérifie la longueur d'un tableau
  • toHaveProperty(key, value): vérifie si un objet a une propriété, et vérifie éventuellement sa valeur
  • toThrowvérifie si une fonction que vous passez lève une exception (en général) ou une exception spécifique
  • toBeInstanceOf(): vérifie si un objet est une instance d'une classe

Tous ces matchers peuvent être annulés en utilisant.not.à l'intérieur de l'instruction, par exemple:

test('Adding 1 + 1 does not equal 3', () => {
  expect(sum(1, 1)).not.toBe(3)
})

Pour une utilisation avec des promesses, vous pouvez utiliser.resolveset.rejects:

expect(Promise.resolve('lemon')).resolves.toBe('lemon')

expect(Promise.reject(new Error(‘octopus’))).rejects.toThrow(‘octopus’)

Installer

Avant d'exécuter vos tests, vous souhaiterez effectuer une initialisation.

Pour faire quelque chose une fois avant l'exécution de tous les tests, utilisez lebeforeAll()fonction:

beforeAll(() => {
  //do something
})

Pour effectuer quelque chose avant chaque test, utilisezbeforeEach():

beforeEach(() => {
  //do something
})

Abattre

Tout comme vous pouvez le faire avec la configuration, vous pouvez également effectuer quelque chose après chaque exécution de test:

afterEach(() => {
  //do something
})

et après la fin de tous les tests:

afterAll(() => {
  //do something
})

Tests de groupe à l'aide de describe ()

Vous pouvez créer des groupes de tests, dans un seul fichier, qui isolent les fonctions de configuration et de démontage:

describe('first set', () => {
  beforeEach(() => {
    //do something
  })
  afterAll(() => {
    //do something
  })
  test(/*...*/)
  test(/*...*/)
})

describe(‘second set’, () => { beforeEach(() => { //do something }) beforeAll(() => { //do something }) test(//) test(//) })

Test du code asynchrone

Le code asynchrone dans JavaScript moderne peut avoir essentiellement 2 formes: les rappels et les promesses. En plus des promesses, nous pouvons utiliser async / await.

Rappels

Vous ne pouvez pas avoir de test dans un rappel, car Jest ne l'exécutera pas - l'exécution du fichier de test se termine avant l'appel du rappel. Pour résoudre ce problème, transmettez un paramètre à la fonction de test, que vous pouvez facilement appelerdone. Jest attendra que vous appeliezdone()avant de terminer ce test:

//uppercase.js
function uppercase(str, callback) {
  callback(str.toUpperCase())
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./src/uppercase’)

test(uppercase 'test' to equal 'TEST', (done) => { uppercase(‘test’, (str) => { expect(str).toBe(‘TEST’) done() } })

Jest async test callback

Promesses

Avec des fonctions qui renvoient des promesses, nousretourner une promessedu test:

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}
module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’) test(uppercase 'test' to equal 'TEST', () => { return uppercase(‘test’).then(str => { expect(str).toBe(‘TEST’) }) })

Jest async test promises

Les promesses rejetées peuvent être testées en utilisant.catch():

//uppercase.js
const uppercase = str => {
  return new Promise((resolve, reject) => {
    if (!str) {
      reject('Empty string')
      return
    }
    resolve(str.toUpperCase())
  })
}

module.exports = uppercase

//uppercase.test.js const uppercase = require(’./uppercase’)

test(uppercase 'test' to equal 'TEST', () => { return uppercase(’’).catch(e => { expect(e).toMatch(‘Empty string’) }) })

Jest async test catch

Asynchroniser / attendre

Pour tester les fonctions qui renvoient des promesses, nous pouvons également utiliser async / await, ce qui rend la syntaxe très directe et simple:

//uppercase.test.js
const uppercase = require('./uppercase')
test(`uppercase 'test' to equal 'TEST'`, async () => {
  const str = await uppercase('test')
  expect(str).toBe('TEST')
})

Jest async test await async

Railleur

En test,railleurvous permet de tester des fonctionnalités qui dépendent de:

  • Base de données
  • Réseaudemandes
  • accès àDes dossiers
  • toutExternesystème

de sorte que:

  1. vos tests sont exécutésplus rapide, donnant un délai d'exécution rapide pendant le développement
  2. vos tests sontindépendantdes conditions du réseau ou de l'état de la base de données
  3. vos tests nepolluertout stockage de données car ils ne touchent pas la base de données
  4. toute modification effectuée dans un test ne change pas l'état des tests suivants, et la réexécution de la suite de tests doit commencer à partir d'un point de départ connu et reproductible
  5. vous n'avez pas à vous soucier de la limitation du débit sur les appels d'API et les demandes réseau

La moquerie est utile lorsque vous voulez éviter les effets secondaires (par exemple l'écriture dans une base de données) ou que vous voulez sauter des portions de code lentes (comme l'accès au réseau), et évite également les implications avec l'exécution de vos tests plusieurs fois (par exemple, imaginez une fonction qui envoie un e-mail ou appelle une API à taux limité).

Plus important encore, si vous écrivez unTest de l'unité, vous devez tester la fonctionnalité d'une fonction de manière isolée, pas avec tout son bagage de choses qu'elle touche.

En utilisant des simulations, vous pouvez vérifier si une fonction de module a été appelée et quels paramètres ont été utilisés, avec:

  • expect().toHaveBeenCalled(): vérifier si une fonction espionnée a été appelée
  • expect().toHaveBeenCalledTimes(): compte combien de fois une fonction espionnée a été appelée
  • expect().toHaveBeenCalledWith(): vérifier si la fonction a été appelée avec un jeu de paramètres spécifique
  • expect().toHaveBeenLastCalledWith(): vérifier les paramètres de la dernière fois que la fonction a été appelée

Espionner les packages sans affecter le code des fonctions

Lorsque vous importez un package, vous pouvez dire à Jest "d'espionner" l'exécution d'une fonction particulière, en utilisantspyOn(), sans affecter le fonctionnement de cette méthode.

Exemple:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const spy = jest.spyOn(mathjs, ‘log’) const result = mathjs.log(10000, 10)

expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

Simulez un paquet entier

Jest fournit un moyen pratique de simuler un package entier. Créer un__mocks__à la racine du projet, et dans ce dossier, créez un fichier JavaScript pour chacun de vos packages.

Dites que vous importezmathjs. Créer un__mocks__/mathjs.jsà la racine de votre projet et ajoutez ce contenu:

module.exports = {
  log: jest.fn(() => 'test')
}

Cela simulera la fonction log () du package. Ajoutez autant de fonctions que vous voulez vous moquer:

const mathjs = require('mathjs')

test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

Simulez une seule fonction

Vous pouvez vous moquer d'une seule fonction en utilisantjest.fn():

const mathjs = require('mathjs')

mathjs.log = jest.fn(() => ‘test’) test(The mathjs log function, () => { const result = mathjs.log(10000, 10) expect(result).toBe(‘test’) expect(mathjs.log).toHaveBeenCalled() expect(mathjs.log).toHaveBeenCalledWith(10000, 10) })

Vous pouvez aussi utiliserjest.fn().mockReturnValue('test')pour créer un simulacre simple qui ne fait rien sauf renvoyer une valeur.

Maquettes pré-construites

Vous pouvez trouver des modèles préfabriqués pour les bibliothèques populaires. Par exemple ce packagehttps://github.com/jefflau/jest-fetch-mockvous permet de vous moquerfetch()appels et fournissez des exemples de valeurs de retour sans interagir avec le serveur réel dans vos tests.

Test de snapshot

Le test d'instantané est une fonctionnalité assez intéressante proposée par Jest. Il peut mémoriser le rendu de vos composants d'interface utilisateur et le comparer au test actuel, ce qui génère une erreur en cas de non-concordance.

Ceci est un test simple sur le composant App d'un simplecreate-react-appapplication (assurez-vous d'installerreact-test-renderer):

import React from 'react'
import App from './App'
import renderer from 'react-test-renderer'

it(‘renders correctly’, () => { const tree = renderer.create(<App />).toJSON() expect(tree).toMatchSnapshot() })

la première fois que vous exécutez ce test, Jest enregistre l'instantané dans le__snapshots__dossier. Voici ce que contient App.test.js.snap:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div
  className="App"
>
  <header
    className="App-header"
  >
    <img
      alt="logo"
      className="App-logo"
      src="logo.svg"
    />
    <h1
      className="App-title"
    >
      Welcome to React
    </h1>
  </header>
  <p
    className="App-intro"
  >
    To get started, edit
    <code>
      src/App.js
    </code>
     and save to reload.
  </p>
</div>
`

Comme vous le voyez, c'est le code que le composant App rend, rien de plus.

La prochaine fois que le test compare la sortie de<App />pour ça. Si l'application change, vous obtenez une erreur:

Error with snapshot

Lors de l'utilisationyarn testdanscreate-react-appvous êtes dansmode montre, et à partir de là, vous pouvez appuyer surwet afficher plus d'options:

Watch Usage
 › Press u to update failing snapshots.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press q to quit watch mode.
 › Press Enter to trigger a test run.

If your change is intended, pressing u will update the failing snapshots, and make the test pass.

You can also update the snapshot by running jest -u (or jest --updateSnapshot) outside of watch mode.


More devtools tutorials: