Introduction : Pourquoi tester du code TypeScript ?
TypeScript est un sur-ensemble de JavaScript qui ajoute le typage statique. Cependant, les types ne garantissent pas une logique applicative correcte. Tester en TypeScript n'est pas simplement une "bonne pratique" mais une nécessité pour maintenir la qualité du code, prévenir les régressions et accélérer le développement.
Dans cet article, nous aborderons trois niveaux principaux de test : unitaires, d'intégration et e2e (de bout en bout). Vous apprendrez quels outils utiliser, verrez des exemples de code et comprendrez comment construire une stratégie de test équilibrée pour votre projet TypeScript.
1. Tests unitaires en TypeScript
Que sont les tests unitaires ?
Les tests unitaires vérifient des modules ou des fonctions isolés. Ils s'exécutent rapidement et ne nécessitent pas de dépendances externes (bases de données, réseau, système de fichiers). L'objectif principal est de s'assurer que chaque petite unité de code fonctionne correctement.
Outils : Jest + ts-jest
Le framework le plus populaire pour les tests unitaires en TypeScript est Jest avec le transformateur ts-jest. Il prend en charge les mocks, les espions, la couverture de code et s'intègre bien avec TypeScript.
Exemple de test unitaire
Supposons que nous ayons une fonction pour calculer une remise :
// discount.tsexport function calculateDiscount(price: number, discountPercent: number): number { if (price < 0 || discountPercent < 0 || discountPercent > 100) { throw new Error('Invalid input'); } return price * (1 - discountPercent / 100);}Écrire le test :
// discount.test.tsimport { calculateDiscount } from './discount';
describe('calculateDiscount', () => { it('should apply 10% discount correctly', () => { expect(calculateDiscount(1000, 10)).toBe(900); });
it('should throw error for negative price', () => { expect(() => calculateDiscount(-100, 10)).toThrow('Invalid input'); });
it('should handle 0% discount', () => { expect(calculateDiscount(500, 0)).toBe(500); });});Exécutez avec : npx jest. Jest trouvera automatiquement tous les fichiers *.test.ts.
Bonnes pratiques pour les tests unitaires
- Isolez les dépendances — utilisez des mocks (jest.mock) pour les modules qui accèdent à la base de données ou à l'API.
- Testez les cas limites — tableaux vides, null, nombres négatifs.
- Suivez le principe FIRST — Rapide (Fast), Isolé (Isolated), Répétable (Repeatable), Auto-validant (Self-validating), Opportun (Timely).
- Utilisez describe/it — pour une structure de test lisible.
2. Tests d'intégration
Que vérifient les tests d'intégration ?
Les tests d'intégration vérifient l'interaction de plusieurs modules : contrôleur + service + base de données, ou point de terminaison API + middleware. Ils sont plus lents que les tests unitaires mais offrent une plus grande confiance dans le couplage correct des composants.
Outils : Supertest + Jest + base de données en mémoire
Pour tester les API Express/Node.js, Supertest (assertions HTTP) est souvent utilisé avec Jest. La base de données peut être exécutée en mémoire (par exemple, mongodb-memory-server ou sqlite3).
Exemple de test d'intégration pour une API Express
Supposons que nous ayons une API REST simple pour gérer les utilisateurs :
// app.tsimport express from 'express';import { userRouter } from './routes/userRoutes';
const app = express();app.use(express.json());app.use('/api/users', userRouter);
export { app };Test avec Supertest :
// user.integration.test.tsimport request from 'supertest';import { app } from '../app';
describe('POST /api/users', () => { it('should create a new user and return 201', async () => {