7 juillet 2026 · NestJS · Tests · Backend
Tester une API NestJS : Jest, mocks et tests e2e avec Supertest
Tests unitaires de services, mock des dépendances via le système d'injection, et tests end-to-end : la stratégie de test d'un backend NestJS.
Le système d'injection de dépendances de NestJS n'est pas qu'une élégance d'architecture : c'est ce qui rend le backend profondément testable. Chaque dépendance déclarée dans un constructeur est un point de substitution pour les tests. Voici comment j'organise la stratégie, du test unitaire au test end-to-end.
Tests unitaires : le module de test et les mocks
NestJS fournit Test.createTestingModule, qui reconstruit un mini-conteneur
d'injection où chaque provider peut être remplacé :
describe('DocumentsService', () => {
let service: DocumentsService;
const prisma = {
document: { findMany: jest.fn(), create: jest.fn() },
};
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
DocumentsService,
{ provide: PrismaService, useValue: prisma },
],
}).compile();
service = module.get(DocumentsService);
jest.clearAllMocks();
});
it('refuse l\'accès à un document d\'un autre patient', async () => {
prisma.document.findMany.mockResolvedValue([]);
await expect(
service.getForPatient('doc-1', 'patient-B'),
).rejects.toThrow(ForbiddenException);
});
});
Le pattern clé : mocker à la frontière (Prisma, services externes,
files d'attente), tester la logique réelle du service. Pas besoin de
mockito ici : les jest.fn() natifs suffisent, et useValue les injecte à
la place des vraies dépendances.
Deux conseils issus de l'expérience :
- Testez les cas d'interdiction autant que les cas de succès. Sur une API qui manipule des données sensibles, les tests les plus précieux sont ceux qui vérifient qu'un utilisateur ne peut PAS accéder à ce qui ne lui appartient pas : guards, ownership, scopes.
- Ne testez pas le framework. Vérifier qu'un décorateur
@Get()route bien une requête, c'est tester NestJS, pas votre code. Votre logique de service et vos règles d'accès, elles, méritent chaque test.
Tests e2e : Supertest contre l'application réelle
Le test end-to-end monte l'application complète (guards, pipes, interceptors, validation) et l'attaque en HTTP via Supertest :
it('POST /auth/verify-otp → 401 sur code invalide', async () => {
await request(app.getHttpServer())
.post('/auth/verify-otp')
.send({ phone: '+242066000000', code: '000000' })
.expect(401);
});
C'est ici qu'on attrape ce que les tests unitaires ne voient pas : un pipe de validation mal configuré, un guard oublié sur une route, un DTO qui laisse passer un champ inattendu. Pour la base de données, deux écoles : une base de test dédiée (fidélité maximale, mes préférences pour les flux critiques) ou le mock du service de données (rapidité). L'important est de choisir consciemment par type de test, pas par accident.
L'organisation qui tient dans le temps
*.spec.tsà côté du code pour l'unitaire,test/*.e2e-spec.tspour le e2e : la convention NestJS, autant la suivre.- Un
beforeEachqui nettoie les mocks (jest.clearAllMocks) : les tests qui dépendent de l'ordre d'exécution sont une dette qui explose toujours. - Les tests e2e couvrent les parcours (inscription → OTP → accès protégé), pas chaque endpoint isolément.
La leçon
La testabilité d'un backend NestJS se décide dans l'architecture : si tout passe par l'injection, tout se substitue, donc tout se teste. Le jour où un test est difficile à écrire, ce n'est presque jamais un problème de test, c'est le code qui révèle un couplage à corriger.