Tests Automatisés : Le Guide Complet du TDD et des Tests Unitaires en 2026
Dans le développement logiciel moderne, une application sans tests est comme un pont sans inspection : tout va bien jusqu’au jour où ça casse. Pourtant, beaucoup de développeurs négligent encore cette pratique, la considérant comme une perte de temps. Rien n’est plus faux.
En 2026, les tests automatisés ne sont plus une option. Ils sont un gage de professionnalisme, un filet de sécurité qui vous permet de refactoriser avec confiance, et un investissement qui rapporte largement sur la durée de vie d’un projet.
Les Fondamentaux des Tests Automatisés
Le Spectre des Tests
Avant de plonger dans la technique, il faut comprendre les différents niveaux de test, souvent représentés par la pyramide des tests :
Tests unitaires (base de la pyramide) : Rapides, peu coûteux, ils testent une unité de code isolée. Ils représentent 70 à 80% de votre base de tests.
Tests d’intégration (milieu) : Ils vérifient que plusieurs modules fonctionnent ensemble : interaction avec une base de données, communication entre services, appels API.
Tests end-to-end (sommet) : Lents et coûteux, ils simulent un parcours utilisateur complet. Ils représentent 5 à 10% de vos tests.
Le TDD : Test-Driven Development
Le TDD est une méthode de développement qui inverse l’ordre traditionnel. Au lieu d’écrire le code puis les tests, on écrit d’abord le test, on le voit échouer (red), on écrit le code minimal pour le faire passer (green), puis on refactorise (refactor). C’est le cycle Red-Green-Refactor.
Cette approche a plusieurs avantages :
- Elle force à réfléchir à l’API de votre code avant de l’implémenter
- Elle garantit que chaque ligne de code est testée
- Elle produit un code plus modulaire et plus facile à tester
- Elle sert de documentation vivante
Exemple Concret avec Vitest et TypeScript
Voici un exemple concret de TDD pour une fonction de validation d’email :
Étape 1 : On écrit le test d’abord
import { describe, it, expect } from 'vitest';
import { validateEmail } from './validation';
describe('validateEmail', () => {
it('devrait accepter un email valide', () => {
expect(validateEmail('user@example.com')).toBe(true);
});
it('devrait rejeter un email sans @', () => {
expect(validateEmail('userexample.com')).toBe(false);
});
it('devrait rejeter un email sans domaine', () => {
expect(validateEmail('user@')).toBe(false);
});
it('devrait rejeter une chaîne vide', () => {
expect(validateEmail('')).toBe(false);
});
});
Étape 2 : Le test échoue (Red)
Étape 3 : On écrit le code minimal
export function validateEmail(email: string): boolean {
if (!email || email.length === 0) return false;
return email.includes('@') && email.includes('.') && email.indexOf('@') > 0;
}
Étape 4 : Le test passe (Green)
Étape 5 : On refactorise (Refactor)
export function validateEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
Les Techniques Essentielles
Le Mocking
Le mocking consiste à remplacer une dépendance réelle (comme une base de données ou une API externe) par une version simulée. Cela permet de tester votre code en isolation.
import { describe, it, expect, vi } from 'vitest';
// Fonction qui appelle une API externe
async function getUserData(userId: string) {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
// Test avec mock de fetch
describe('getUserData', () => {
it('devrait retourner les données utilisateur', async () => {
const mockUser = { id: '1', name: 'Alice' };
globalThis.fetch = vi.fn().mockResolvedValue({
json: () => Promise.resolve(mockUser),
});
const result = await getUserData('1');
expect(result).toEqual(mockUser);
expect(fetch).toHaveBeenCalledWith('/api/users/1');
});
});
Les Tests Paramétrés
Pour éviter la duplication de tests similaires, utilisez les tests paramétrés :
describe('validateEmail', () => {
it.each([
['user@example.com', true],
['userexample.com', false],
['user@', false],
['', false],
['user@.com', false],
['user.name+tag@example.co.uk', true],
])('validateEmail("%s") devrait retourner %s', (email, expected) => {
expect(validateEmail(email)).toBe(expected);
});
});
Intégration des Tests dans le Workflow
Pour que les tests soient vraiment utiles, ils doivent être intégrés dans votre pipeline de développement :
Avant le Commit : Les Hooks Pre-commit
Utilisez Husky ou Lefthook pour exécuter les tests avant chaque commit :
# .husky/pre-commit
npx vitest run --changed
Dans le CI/CD : Tests Automatiques
Chaque push sur la branche principale doit déclencher une exécution complète des tests, comme nous le couvrons dans notre guide CI/CD.
# .github/workflows/test.yml
name: Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npx vitest run --coverage
Les Pièges à Éviter
1. Tester du Code Trivial
Évitez de tester des getters/setters ou du code framework. Testez la logique métier, pas l’infrastructure.
2. Tests Fragiles
Des tests qui cassent au moindre changement sont contre-productifs. Testez le comportement, pas l’implémentation. Préférez expect(result).toBe(expected) à expect(implementationDetail).toBeCalledWith(...).
3. Mauvaise Isolation des Tests
Chaque test doit être indépendant. Utilisez beforeEach pour réinitialiser l’état entre chaque test.
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
});
4. Tests Trop Lents
Si votre suite de tests prend plus de 5 minutes, il y a un problème. Utilisez le mocking pour éviter les appels réseau et les accès disque dans les tests unitaires.
Les Outils en 2026
- Vitest : Framework de test rapide et moderne pour TypeScript/JavaScript
- Playwright : Tests end-to-end cross-navigateur
- Supertest : Tests d’API HTTP pour Express/Fastify
- MSW (Mock Service Worker) : Mocking d’API au niveau réseau
- C8/Istanbul : Couverture de code
Conclusion
Les tests automatisés ne sont pas une corvée : ils sont un investissement dans la qualité et la maintenabilité de votre code. Que vous adoptiez le TDD strict ou une approche plus pragmatique, l’essentiel est de commencer. Écrivez un test aujourd’hui pour une fonction critique de votre application en suivant les principes du typage fort avec TypeScript. Demain, vous dormirez mieux.
Pour aller plus loin, explorez notre guide sur les workflows Git pour le développement en équipe et découvrez comment intégrer les tests dans votre pipeline CI/CD pour une qualité automatisée de bout en bout.