Tests avancés en TypeScript : de l'Unit à l'E2E
Les tests ne sont pas simplement une vérification du bon fonctionnement du code, mais le fondement d'un développement professionnel en TypeScript. Au niveau avancé, nous passons des simples assert-ions à la construction de pyramides de tests isolées, rapides et fiables. Dans cet article, nous analyserons les approches pratiques pour les tests unitaires, d'intégration et e2e en utilisant les meilleurs outils de l'écosystème TypeScript.
1. Tests Unitaires Avancés avec Jest et TypeScript
Les tests unitaires vérifient des modules isolés. En TypeScript, la difficulté clé réside dans la gestion des dépendances, des types et de l'asynchronisme.
1.1. Mocks et typage avec jest.mocked
Utilisez jest.mocked pour un typage complet des mocks. Cela garantit que vous n'appellerez pas une méthode inexistante.
import { fetchUserData } from './api';import { processUser } from './userProcessor';
jest.mock('./api');
const mockedFetchUserData = jest.mocked(fetchUserData);
describe('processUser', () => { it('doit traiter les données utilisateur', async () => { const mockUser = { id: 1, name: 'Alice', email: 'alice@test.com' }; mockedFetchUserData.mockResolvedValue(mockUser);
const result = await processUser(1);
expect(result).toEqual({ displayName: 'Alice', contact: 'alice@test.com' }); expect(mockedFetchUserData).toHaveBeenCalledWith(1); });
it('doit lever une erreur si l\\'email est absent', async () => { mockedFetchUserData.mockResolvedValue({ id: 2, name: 'Bob', email: null }); await expect(processUser(2)).rejects.toThrow('Email is required'); });});1.2. Test de types complexes et generics
Pour les fonctions avec des generics, créez des types helpers de test pour éviter la duplication.
// Codefunction getFirstElement<T>(arr: T[]): T | undefined { return arr[0];}
// Testtype TestArray = number[];const array: TestArray = [10, 20, 30];
describe('getFirstElement', () => { it('doit retourner le premier élément numérique', () => { const result = getFirstElement(array); expect(result).toBe(10); // TypeScript déduit automatiquement le type number });
it('doit retourner undefined pour un tableau vide', () => { const result = getFirstElement([]); expect(result).toBeUndefined(); });});2. Tests d'Intégration : Base de données et API
Les tests d'intégration vérifient l'interaction des modules : contrôleurs, services et base de données. Pour les applications TypeScript sur Node.js, la combinaison Jest + Supertest + Testcontainers est idéale.
2.1. Configuration de la base de données de test avec Testcontainers
Testcontainers démarre PostgreSQL dans Docker, garantissant un environnement propre pour chaque test.
import { PostgreSqlContainer, StartedPostgreSqlContainer } from '@testcontainers/postgresql';import { Client } from 'pg';
let container: StartedPostgreSqlContainer;let client: Client;
beforeAll(async () => { container = await new PostgreSqlContainer().start(); client = new Client({ connectionString: container.getConnectionUri() }); await client.connect(); await client.query(` CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(100) UNIQUE NOT NULL ); `);}, 30000); // timeout de 30 secondes pour le téléchargement de l'image
afterAll(async () => { await client.end(); await container.stop();});
describe('User Repository', () => { it('doit créer et retourner un utilisateur', async () => { const res = await client.query( `INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *`, ['Test', 'test@example.com'] ); expect(res.rows[0].name).toBe('Test'); });});2.2. Test de l'API REST avec Supertest
Lancez l'application Express et test