Dans ce chapitre je vous propose de découvrir ensemble comment tester vos composants React à l'aide de la librairie Jest et de Testing Library. Tester permet de s'assurer que les composants fonctionnent comme attendu et permet aussi d'éviter d'introduire des bugs lors que l'on effectue un refactoring.
Environnement de test
Avec create-react-app
Si vous utilisez le template create-react-app
vous n'avez pas grand chose à faire étant donné que l'environnement de test est déjà configuré pour vous. Il vous suffit de lancer la commande npm run test
pour lancer les tests. Vous pouvez vous inspirer du fichier App.test.js
pour voir à quoi ressemble un test par défaut.
Sans create-react-app
Cependant si on utilise react dans un projet qui n'utilise pas ce template il est important d'être en mesure de configurer les choses soi-même. Pour tester le code nous allons avoir besoin de plusieurs éléments
- Jest, qui servira de framework de test (test runner + assertions)
- Babel, qui permettra de convertir le code JSX en code JavaScript qui pourra être compris par Node (via les presets
@babel/preset-env
et@babel/preset-react
). - Testing Library, qui nous offrira des helpers pour monter / démonter facilement nos éléments et qui ajoutera des assertions sur le DOM.
On commence donc par installer les différentes librairies
npm i jest @types/jest babel-jest @babel/preset-env @babel/preset-react @testing-library/dom @testing-library/jest-dom @testing-library/react --save-dev
On crée ensuite notre configuration babel via un fichier babel.config.js
à la racine de notre projet :
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-react']
}
On configure aussi jest via un fichier jest.config.js
pour ajouter les assertions du DOM
module.exports = {
setupFilesAfterEnv: [
'./src/setupTest.js'
]
}
Et dans le fichier setupTest.js
on charge étend expect
import '@testing-library/jest-dom/extend-expect'
Maintenant vous pouvez lancer vos tests en utilisant la commande npx jest
.
Ecrire son premier test
Tester un composant
Pour écrire un test il suffit de monter un composant puis de voir que la structure est bien celle attendue (on testera souvent la présence d'un élément ou de texte). Même si on manipule le DOM il n'est pas nécessaire d'avoir recours à un navigateur car nos tests vont se reposer sur jsdom une implémentation des standards qui fonctionne directement sur NodeJS. Cependant, pour des cas plus spécifiques vous pourriez avoir besoin d'outil comme jest-puppeteer pour intéragir avec un environnement plus réel.
import { Modal } from './Modal'
import { render, fireEvent } from '@testing-library/react'
import React from 'react'
import { screen } from '@testing-library/dom'
test("Le titre devrait s'afficher", function () {
render(<Modal title="Bonjour les gens" onClose={() => null}>Bonjour</Modal>)
const title = screen.getByText('Bonjour les gens')
expect(title).toBeInTheDocument()
})
test('Le callback de fermeture est appelé lors du clic sur x', function () {
const mockClose = jest.fn()
render(<Modal title="Bonjour les gens" onClose={mockClose}>Bonjour</Modal>)
const close = document.body.querySelector("[aria-label='Fermer']")
fireEvent.click(close)
expect(mockClose.mock.calls.length).toBe(1)
})
test('Le callback de fermeture est appelé avec échap', function () {
const mockClose = jest.fn()
render(<Modal title="Bonjour les gens" onClose={mockClose}>Bonjour</Modal>)
fireEvent.keyDown(document, { key: 'Escape' })
expect(mockClose.mock.calls.length).toBe(1)
})
test("Le callback de fermeture n'est pas appelé avec une autre touché qu'échap", function () {
const mockClose = jest.fn()
render(<Modal title="Bonjour les gens" onClose={mockClose}>Bonjour</Modal>)
fireEvent.keyDown(document, { key: 'Enter' })
expect(mockClose.mock.calls.length).toBe(0)
})
Tester un hook
Les hooks permettent de représenter les changements d'états et peuvent parfois contenir une logique que l'on va souhaiter tester. Malheureusement, un hook ne peut pas être appelé directement et il nous faudra un environnement spécial pour pouvoir les éxécuter. Vous pouvez utiliser @testing-library/react-hooks
pour cela.
npm i @testing-library/react-hooks react-test-renderer --save-dev
Ensuite il vous suffit d'utiliser les méthodes renderHook()
et act()
lorsque vous interagissez avec votre hook personnalisé.
import { useToggle } from "./hooks"
import { renderHook, act } from '@testing-library/react-hooks'
// Le hook fonctionne comme cela
// const [visible, toggleVisible] = useToggle(false)
test('toggleHook', function () {
const { result } = renderHook(() => useToggle(false))
// result.current contiendra le retour du hook
expect(result.current[0]).toBeFalsy()
act(() => result.current[1]())
expect(result.current[0]).toBeTruthy()
act(() => result.current[1]())
expect(result.current[0]).toBeFalsy()
})