Test en JavaScript : Tests Unitaires, d'Intégration et E2E
Le test est une partie essentielle du développement de tout logiciel de qualité. Dans le monde de JavaScript (en particulier dans l'écosystème Node.js), il existe trois niveaux principaux de test : les tests unitaires, les tests d'intégration et les tests E2E (de bout en bout). Chacun résout ses propres problèmes et est utilisé à différentes étapes du développement. Dans cet article, nous allons détailler chaque type, montrer des exemples de code et donner des recommandations pratiques.
1. Tests Unitaires
Les tests unitaires vérifient des unités de code individuelles et isolées — fonctions, méthodes ou classes. L'objectif principal est de s'assurer que chaque « unité » fonctionne correctement indépendamment du reste du système. Dans Node.js, on utilise le plus souvent les frameworks Jest ou Mocha avec Chai.
Exemple de test unitaire avec Jest
Supposons que nous ayons une fonction simple pour calculer une remise :
// discount.jsfunction calculateDiscount(price, percent) { if (typeof price !== 'number' || typeof percent !== 'number') { throw new Error('Les deux arguments doivent être des nombres'); } if (price < 0 || percent < 0 || percent > 100) { throw new Error('Valeurs incorrectes'); } return price - (price * percent / 100);}
module.exports = calculateDiscount;Test pour cette fonction :
// discount.test.jsconst calculateDiscount = require('./discount');
describe('calculateDiscount', () => { test('calcule correctement une remise de 10% sur 1000', () => { expect(calculateDiscount(1000, 10)).toBe(900); });
test('retourne 0 pour une remise de 100%', () => { expect(calculateDiscount(500, 100)).toBe(0); });
test('lève une erreur pour un prix négatif', () => { expect(() => calculateDiscount(-100, 10)).toThrow('Valeurs incorrectes'); });
test('lève une erreur pour des arguments non numériques', () => { expect(() => calculateDiscount('100', 10)).toThrow('Les deux arguments doivent être des nombres'); });});- Avantages : Rapides, faciles à écrire, identifient précisément le module problématique.
- Inconvénients : Ne vérifient pas les interactions entre les composants.
- Quand les utiliser : Pour les fonctions de logique métier, les utilitaires, les validateurs.
2. Tests d'Intégration
Les tests d'intégration vérifient comment différents modules ou services fonctionnent ensemble. Par exemple, l'interaction avec une base de données, une API externe ou le système de fichiers. Dans Node.js, on utilise souvent Supertest (pour les requêtes HTTP) et Jest pour ces tests.
Exemple de test d'intégration pour une application Express
Supposons que nous ayons un simple serveur Express avec un endpoint pour créer un utilisateur :
// server.jsconst express = require('express');const app = express();app.use(express.json());
const users = [];
app.post('/users', (req, res) => { const { name, email } = req.body; if (!name || !email) { return res.status(400).json({ error: 'Name and email are required' }); } const user = { id: users.length + 1, name, email }; users.push(user); res.status(201).json(user);});
module.exports = app;Test d'intégration avec Supertest :
// server.test.jsconst request = require('supertest');const app = require('./server');
describe('POST /users', () => { test('crée un nouvel utilisateur avec des données valides', async () => { const response = await request(app) .post('/users') .send({ name: 'Ivan', email: 'ivan@example.com' });
expect(response.statusCode).toBe(201); expect(response.body).toHaveProperty('id'); expect(response.bo