
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 et d'éviter les régressions.
Imaginez que vous travaillez sur une application avec plusieurs fonctionnalités. Vous modifiez du code pour ajouter une nouvelle fonctionnalité, et sans vous en rendre compte, vous cassez quelque chose qui fonctionnait parfaitement. C'est ce qu'on appelle une régression.
Les tests automatisés vous alertent immédiatement quand quelque chose ne va plus. Vous gagnez en confiance et en rapidité de développement.
Il existe trois types principaux de tests :
Tests unitaires : Testent une petite partie isolée du code (une fonction, par exemple). Rapides à écrire, mais ne garantissent pas que tout fonctionne ensemble.
Tests d'intégration : Testent une fonctionnalité complète en simulant des interactions utilisateur. Bon compromis entre sécurité et temps d'écriture.
Tests end-to-end (E2E) : Testent l'application de bout en bout, comme un véritable utilisateur. Très sécurisants mais longs à écrire et maintenir.
Dans ce cours, nous nous concentrerons sur les tests unitaires et d'intégration avec Vitest et React Testing Library.
Pourquoi Vitest plutôt que Jest d'ailleurs ?
Vitest est l'outil de test recommandé pour les projets Vite. Il est extrêmement rapide, compatible avec l'écosystème Jest, et nécessite très peu de configuration.
Dans votre projet React créé avec Vite, installez Vitest :
npm install -D vitest
Puis ajoutez un script de test dans votre package.json :
{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
}
}Créons un fichier src/utils/formatters.js avec une fonction simple :
/**
* Formate un prix en euros
* @param {number} price - Le prix à formater
* @returns {string} Le prix formaté (ex: "19,99 €")
*/
export function formatPrice(price) {
return `${price.toFixed(2).replace('.', ',')} €`
}
/**
* Formate une liste d'éléments avec des virgules
* @param {string} item - L'élément à formater
* @param {number} index - L'index de l'élément
* @param {number} totalLength - La longueur totale du tableau
* @returns {string} L'élément avec ou sans virgule
*/
export function formatListItem(item, index, totalLength) {
if (index === totalLength - 1) {
return item
}
return `${item},`
}Vitest cherche automatiquement les fichiers qui se terminent par :
.test.js
.test.jsx
.spec.js
.spec.jsx
Créez le fichier src/utils/formatters.test.js :
import { describe, it, expect } from 'vitest'
import { formatPrice, formatListItem } from './formatters'
describe('formatPrice', () => {
it('should format a price correctly', () => {
const result = formatPrice(19.99)
expect(result).toBe('19,99 €')
})
it('should add two decimals if needed', () => {
const result = formatPrice(20)
expect(result).toBe('20,00 €')
})
it('should handle decimals correctly', () => {
const result = formatPrice(9.5)
expect(result).toBe('9,50 €')
})
})
describe('formatListItem', () => {
it('should add a comma to an item that is not the last', () => {
const result = formatListItem('React', 0, 3)
expect(result).toBe('React,')
})
it('should not add a comma to the last item', () => {
const result = formatListItem('TypeScript', 2, 3)
expect(result).toBe('TypeScript')
})
})Un test avec Vitest suit cette structure :
describe(): Groupe plusieurs tests liés entre eux
it()outest(): Définit un test individuel (ce sont des alias, vous pouvez utiliser l'un ou l'autre)
expect(): Définit ce qu'on s'attend à obtenir
Matchers :.toBe(),.toEqual(),.toContain(), etc. qui viennent vérifier les valeurs
describe('Nom du groupe de tests', () => {
it('should do something', () => {
// 1. Préparer les données (Arrange)
const input = 'test'
// 2. Exécuter la fonction (Act)
const result = maFonction(input)
// 3. Vérifier le résultat (Assert)
expect(result).toBe('expected')
})
})npm run test
Vous devriez voir tous vos tests passer en vert !
Vitest (comme Jest) offre de nombreux matchers pour vos assertions :
// Égalité stricte
expect(value).toBe(5)
expect(value).not.toBe(10)
// Égalité profonde (pour objets/tableaux)
expect(object).toEqual({ name: 'React' })
// Valeurs truthful/falsy
expect(value).toBeTruthy()
expect(value).toBeFalsy()
expect(value).toBeNull()
expect(value).toBeUndefined()
expect(value).toBeDefined()
// Nombres
expect(value).toBeGreaterThan(10)
expect(value).toBeLessThan(20)
expect(value).toBeCloseTo(0.3) // Pour les décimaux
// Chaînes
expect(string).toMatch(/pattern/)
expect(string).toContain('substring')
// Tableaux
expect(array).toContain('item')
expect(array).toHaveLength(3)
// Fonctions
expect(fn).toThrow()
expect(fn).toThrow('error message')La couverture de tests (code coverage) mesure le pourcentage de votre code qui est exécuté par vos tests.
npm install -D @vitest/coverage-v8
npm run test:coverage
Vous verrez un tableau détaillé dans votre terminal :
% Coverage report from v8
-------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files | 35 | 60 | 50 | 35 |
src | 0 | 0 | 0 | 0 |
App.jsx | 0 | 0 | 0 | 0 | 1-35
main.jsx | 0 | 0 | 0 | 0 | 1-10
src/utils | 100 | 100 | 100 | 100 |
...ters.js | 100 | 100 | 100 | 100 |
-------------|---------|----------|---------|---------|-------------------
PASS Waiting for file changes...
Analisons ce que nous trouvons dans ce tableau :
File : Indique le fichier testé
% stmts: Indique le pourcentage d'instructions (Statements) qui ont été testées
% branch: Mesure le pourcentage de branches conditionnelle (if/else, switch etc..) qui ont été testé.
% func: Mesure le pourcentage des fonctions qui ont été appelées au moins une fois dans vos tests
% Lines: Mesure le pourcentage de lignes de code qui ont été exécutées. C'est similaire à statements mais compte les lignes physiques plus que les instructions logiques.
Uncovered Line #s: Ce sont le numero des lignes qui n'on pas été testés.
Un fichier HTML détaillé est également généré dans
coverage/index.html .

Dans la branche P1C2-Begin vous trouverez un fichiersrc/utils/validators.js avec les fonctions suivantes :
export function isEmailValid(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
export function isPasswordStrong(password) {
// Au moins 8 caractères, une majuscule, une minuscule, un chiffre
return password.length >= 8 &&
/[A-Z]/.test(password) &&
/[a-z]/.test(password) &&
/[0-9]/.test(password)
}Créez le fichier validators.test.js et écrivez au moins 5 tests :
2 tests pourisEmailValid(un email valide et un invalide)
3 tests pourisPasswordStrong(fort, faible, cas limites)
Les tests permettent d'éviter les régressions et de gagner en confiance
Les tests d'intégration sont un bon compromis entre temps et sécurité
Vitest est l'outil moderne pour tester les applications React avec Vite
describe()groupe les tests,it()définit un test,expect()vérifie le résultat
Le coverage mesure le pourcentage de code testé, mais la qualité prime sur la quantité
Dans le prochain chapitre, nous allons découvrir React Testing Library pour tester nos composants React.