describe, it, expect â tester comme un pro
Bootcode IWA-S04 â Semaine 15, Jour 2
1. Ăcrire des tests complets
Pour un service métier avec describe et it
2. Injecter un InMemoryRepository
Tester sans base de données
3. Tester les cas d'erreur
Avec toThrow pour les exceptions
4. Organiser avec beforeEach
Chaque test repart de zéro
La syntaxe Jest : describe, it, expect
Structurer ses tests comme des phrases
Les matchers essentiels
toBe, toEqual, toThrow, toContain
toBe vs toEqual
Primitifs vs objets â l'erreur classique
beforeEach : préparer les données
Chaque test repart de zéro
Tester les erreurs avec toThrow
Attention Ă la syntaxe â passer une fonction
Module 1
describe, it, expect â structurer ses tests
Un test = une phrase lisible par un humain
describe('UserService', () => {
it('should create a user with a valid email', () => {
const user = createUser('[email protected]');
expect(user.email).toBe('[email protected]');
});
});
đĄ Lit la phrase Ă voix haute :
"UserService should create a user with a valid email" â ça a du sens !
// describe = un bloc, un "sujet" de test
describe('UserService', () => {
// tous les tests liés à UserService
it('should create a user', () => { ... });
it('should delete a user', () => { ... });
it('should throw on invalid email', () => { ... });
});
describe
Le "sujet"
it
Le "comportement"
expect
L'"assertion"
đĄ On peut imbriquer des describe pour organiser : describe('UserService') â describe('create()')
it('should add two numbers', () => {
// 1ïžâŁ Arrange â prĂ©parer
const a = 2, b = 3;
// 2ïžâŁ Act â exĂ©cuter
const result = add(a, b);
// 3ïžâŁ Assert â vĂ©rifier
expect(result).toBe(5);
});
đ Le pattern AAA : Arrange, Act, Assert
Chaque it suit ce schĂ©ma â un seul comportement testĂ© par it
đĄ Le nom du it commence par "should" â lisible comme une phrase en anglais
// expect(valeur) .matcher(valeurAttendue)
expect(add(2, 3)).toBe(5);
// expect reçoit la valeur réelle, le matcher reçoit l'attendu
expect(user.email).toBe('[email protected]');
expect(valeur)
La valeur réelle produite par ton code
.matcher(attendu)
La comparaison + la valeur qu'on espĂšre
đĄ On peut enchaĂźner plusieurs expect dans un mĂȘme it, mais un seul comportement testĂ©
Module 2
toBe, toEqual, toContain, toThrow
// toBe = === (strict equality)
expect(1 + 1).toBe(2);
expect('hello').toBe('hello');
expect(true).toBe(true);
// Pour les primitifs : number, string, boolean
toBe compare la valeur ET le type â comme === en JavaScript
â ïž toBe sur un objet = compare la RĂFĂRENCE
Deux objets identiques mais séparés ne sont pas toBe !
// toEqual compare le CONTENU, pas la référence
const a = { nom: 'Alice' };
const b = { nom: 'Alice' };
expect(a).toBe(b); // â rĂ©fĂ©rences diffĂ©rentes
expect(a).toEqual(b); // â mĂȘme contenu
toBe
Référence (identité)
a === b ?
toEqual
Contenu (valeur)
MĂȘmes clĂ©s/valeurs ?
đĄ RĂšgle d'or : toBe pour les primitifs, toEqual pour les objets et tableaux
// Dans un tableau
expect(['Alice', 'Bob']).toContain('Alice');
// Dans une chaĂźne
expect('Hello World').toContain('World');
Tableaux
L'élément est-il présent ?
ChaĂźnes
La sous-chaßne est-elle présente ?
đĄ Pratique pour vĂ©rifier qu'un utilisateur est dans une liste, ou qu'un message d'erreur contient un mot-clĂ©
Module 3
Primitifs vs objets â l'erreur classique
â toBe sur un objet
const user1 = { nom: 'Alice' };
const user2 = { nom: 'Alice' };
expect(user1).toBe(user2);
// â ĂCHEC !
// Deux objets distincts
// Pas la mĂȘme rĂ©fĂ©rence
â toEqual sur un objet
const user1 = { nom: 'Alice' };
const user2 = { nom: 'Alice' };
expect(user1).toEqual(user2);
// â SUCCĂS !
// MĂȘme contenu
// Comparaison profonde
đ toBe = === (mĂȘme rĂ©fĂ©rence) · toEqual = comparaison profonde du contenu
// Pour les primitifs, toBe et toEqual sont équivalents
expect(42).toBe(42); // â
expect(42).toEqual(42); // â
// Mais pour les objets, ça change tout !
expect({ a: 1 }).toBe({ a: 1 }); // â
expect({ a: 1 }).toEqual({ a: 1 }); // â
đ Convention de l'Ă©cosystĂšme :
Module 4
PrĂ©parer les donnĂ©es â chaque test repart de zĂ©ro
describe('UserService', () => {
let repo, service;
it('should add Alice', () => {
repo = new InMemoryRepo();
service = new UserService(repo);
service.create('Alice');
expect(repo.count()).toBe(1);
});
it('should add Bob', () => {
// đ± Il faut tout recrĂ©er Ă chaque fois !
repo = new InMemoryRepo();
service = new UserService(repo);
service.create('Bob');
expect(repo.count()).toBe(1);
});
});
â ïž Code dupliquĂ© Ă chaque test â et si un test modifie l'Ă©tat, le suivant hĂ©rite du dĂ©sordre !
describe('UserService', () => {
let repo, service;
beforeEach(() => {
// Exécuté AVANT chaque it
repo = new InMemoryRepo();
service = new UserService(repo);
});
it('should add Alice', () => {
service.create('Alice');
expect(repo.count()).toBe(1);
});
it('should add Bob', () => {
service.create('Bob');
expect(repo.count()).toBe(1); // â repart de zĂ©ro
});
});
â Chaque it dĂ©marre avec un Ă©tat frais â indĂ©pendance garantie !
Module 5
Attention Ă la syntaxe â passer une fonction !
â Mauvaise syntaxe
// La fonction est exécutée
// AVANT que expect la voie
expect(createUser(''))
.toThrow('Invalid email');
// â L'erreur est lancĂ©e
// AVANT le expect !
// Le test crash au lieu de passer
â Bonne syntaxe
// On passe une FONCTION
// Jest l'appelle lui-mĂȘme
expect(() => createUser(''))
.toThrow('Invalid email');
// â Jest capture l'erreur
// et vérifie le message
đ expect(() => fn()).toThrow() â toujours passer une FONCTION, pas le rĂ©sultat
// Vérifier qu'une erreur est lancée (sans message)
expect(() => createUser('')).toThrow();
// Vérifier le message exact
expect(() => createUser('')).toThrow('Invalid email');
// Vérifier avec une RegExp
expect(() => createUser('')).toThrow(/invalid/i);
// Vérifier le type d'erreur
expect(() => createUser('')).toThrow(ValidationError);
đĄ toThrow sans argument = juste vĂ©rifier qu'une erreur est lancĂ©e. Avec argument = vĂ©rifier le message ou le type.
describe('UserService', () => {
let repo, service;
beforeEach(() => {
repo = new InMemoryRepo();
service = new UserService(repo);
});
describe('create()', () => {
it('should create a user with valid email', () => {
const user = service.create('[email protected]');
expect(user.email).toBe('[email protected]');
expect(user.id).toBeDefined();
});
it('should throw on empty email', () => {
expect(() => service.create('')).toThrow('Invalid email');
});
});
});
1ïžâŁ beforeEach
Ătat frais
2ïžâŁ Cas de succĂšs
toBe / toEqual
3ïžâŁ Cas d'erreur
toThrow
â toBe pour comparer des objets
Ăa compare les rĂ©fĂ©rences, pas le contenu â utiliser toEqual
â expect(fn()).toThrow()
La fonction s'exĂ©cute avant le expect â utiliser expect(() => fn()).toThrow()
â Tests qui dĂ©pendent de l'ordre
Un test modifie l'Ă©tat, le suivant en dĂ©pend â toujours utiliser beforeEach
â Plusieurs comportements par it
Un it = un comportement â sinon les erreurs sont difficiles Ă diagnostiquer
đ
describe / it / expect
Sujet · comportement · assertion
âïž
toBe vs toEqual
Primitifs (===) vs objets (profond)
đŻ
toThrow = une fonction
expect(() => fn()).toThrow()
đ
beforeEach = indépendance
Chaque test repart de zéro
Tester, c'est coder en confiance
Prochaine étape : tester une vraie API avec mocks et spies