Dans le chapitre précédent, votre tech lead vous a confié une première mission importante : sécuriser le service d'authentification de l'application de gestion de tâches. Pour cela, vous avez écrit vos premiers tests unitaires sur la fonction
hashPassword(). Vous avez appris à isoler une fonction, à structurer un test avec la méthode AAA (Arrange, Act, Assert) et à interpréter les messages d'erreur affichés par Vitest.
C'est précisément ce que nous allons tester dans ce chapitre. Vous allez quitter le monde des fonctions isolées pour vérifier le comportement complet de vos premières routes API Next.js. Vous découvrirez comment appeler directement les route handlers dans Vitest, comment préparer des données de test fiables et comment reproduire un véritable parcours utilisateur allant de la connexion à la création d'une tâche.
Dans une application professionnelle, il est rare qu'une fonctionnalité repose sur une seule fonction. Plusieurs composants collaborent généralement pour répondre à une action utilisateur. C'est pourquoi les tests unitaires ne suffisent pas toujours.
Prenons l'exemple de la routePOST /api/auth/loginde notre application fil rouge. Lorsqu'un utilisateur tente de se connecter, plusieurs opérations sont réalisées : récupération des informations envoyées dans la requête, recherche de l'utilisateur, vérification du mot de passe, génération d'un jeton d'authentification et construction de la réponse HTTP. Tester uniquement la fonction de hachage ne permet pas de savoir si l'ensemble de ce processus fonctionne correctement.
Le test d'intégration cherche donc à répondre à plusieurs questions :
La route retourne-t-elle le bon code HTTP ?
La structure de la réponse est-elle correcte ?
Les données attendues sont-elles présentes ?
Les règles métier sont-elles respectées ?
Dans notre application de gestion de tâches, deux routes constituent des candidats idéaux pour découvrir ce niveau de test :
POST /api/auth/login, qui permet à un utilisateur de s'authentifier ;
POST /api/tasksetGET /api/tasks, qui permettent de créer puis de consulter des tâches.
En reprenant la boussole des quatre niveaux de test découverte dans P1C2, nous passons donc du niveau « test unitaire » au niveau « intégration back-end ». Cette progression est naturelle : après avoir validé qu'une fonction fonctionne seule, nous vérifions maintenant qu'elle s'intègre correctement dans un flux métier complet.
Lorsque l'on découvre les tests d'intégration, on imagine souvent qu'il faut démarrer un serveur, envoyer de vraies requêtes réseau et installer plusieurs bibliothèques spécialisées. Avec Next.js App Router, la réalité est beaucoup plus simple.
Supposons que nous souhaitions tester la route de connexion. La première étape consiste à importer le handler :
// @vitest-environment node
import { POST } from "@/app/api/auth/login/route";La directive// @vitest-environment node, placée en première ligne, demande à Vitest d’exécuter ce fichier dans l’environnement Node plutôt que jsdom. Ces tests s’exécutent côté serveur, et certaines bibliothèques comme jose (qui signe le jeton JWT) ne fonctionnent pas sous jsdom.
Nous pouvons ensuite créer une requête similaire à celle qu'enverrait un navigateur :
const req = new Request(
"http://localhost/api/auth/login",
{
method: "POST",
body: JSON.stringify({
email: "test@example.com",
password: "secret"
}),
headers: {
"Content-Type": "application/json"
}
}
);Une fois la requête créée, il suffit d'appeler la fonction :
const res = await POST(req);Nous obtenons alors un objetResponsesur lequel nous pouvons effectuer nos vérifications :
expect(res.status).toBe(200);
const body = await res.json();
expect(body.token).toBeDefined();Cette approche présente plusieurs avantages. D'abord, les tests sont rapides puisqu'aucun serveur n'est démarré. Ensuite, la configuration reste très légère : Vitest suffit. Enfin, les tests restent proches du comportement réel de l'application, car ils utilisent exactement les mêmes objetsRequestetResponseque ceux manipulés en production.
Autrement dit, nous simulons une requête HTTP sans avoir besoin de toute l'infrastructure réseau habituellement associée aux tests d'API. C'est ce qui rend les tests d'intégration Next.js particulièrement agréables à écrire.
Maintenant que nous savons appeler une route API, une nouvelle question apparaît : comment garantir que chaque test s'exécute dans des conditions prévisibles ?
Imaginons que nous souhaitions tester la connexion d'un utilisateur. Si aucun utilisateur n'existe dans notre environnement de test, la connexion échouera forcément. À l'inverse, si un test précédent a laissé des données derrière lui, nous risquons d'obtenir des résultats incohérents.
C'est pour éviter ce problème que nous préparons systématiquement les données nécessaires avant l'exécution d'un test.
Vitest fournit un outil particulièrement utile pour cela :beforeEach().
La fonctionbeforeEach()est exécutée avant chaque test. Elle permet de remettre l'environnement dans un état connu. Dans notre application, la couche de données vit en mémoire et expose une fonctionresetStore()qui réinitialise le store à son état de départ : un utilisateur de démonstration (test@example.com/secret) et quelques tâches sont alors de nouveau disponibles.
import { resetStore } from "@/lib/data/store";
beforeEach(() => {
resetStore();
});Comme l'utilisateur de test est recréé par le seed à chaque réinitialisation, nul besoin de l'ajouter ou de le supprimer manuellement. Cette organisation apporte un avantage majeur : chaque test démarre dans un état connu.
Prenons un exemple concret. Imaginons un premier test qui crée une tâche pour l'utilisateur connecté et un second qui vérifie que sa liste de tâches est vide au départ. Si le premier test laisse sa tâche derrière lui dans le store en mémoire, le second pourrait échouer simplement parce qu'une tâche supplémentaire serait présente. Pourtant, le problème ne viendrait pas du code testé. Grâce àbeforeEach()et àresetStore(), chaque scénario dispose de son propre environnement et reste totalement indépendant des autres.
Avant de construire ce parcours complet, observez dans ce screencast comment tester une connexion réussie, gérer un cas d’échec, puis réutiliser le token JWT pour créer et récupérer des tâches via les routes API.
À vous maintenant de mettre en place ce scénario d’intégration étape par étape.
Votre tech lead souhaite maintenant valider un véritable parcours métier. La première étape consiste à tester la routePOST /api/auth/login. Nous devons vérifier deux situations : la connexion réussie et la connexion échouée. Dans le premier cas, un utilisateur valide doit recevoir un jeton d'authentification. Dans le second, la route doit renvoyer une erreur indiquant que les identifiants sont incorrects.
Une fois cette étape validée, nous pouvons passer à un scénario plus réaliste. Après sa connexion, un utilisateur crée une tâche puis consulte sa liste de tâches. Ce parcours implique plusieurs routes qui collaborent entre elles.
Le scénario complet se déroule ainsi :
L'utilisateur se connecte.
La route renvoie un jeton JWT. Ce jeton est ajouté dans l'en-têteAuthorization.
Une requête est envoyée àPOST /api/tasks.
Une seconde requête est envoyée àGET /api/tasks.
La tâche créée apparaît dans la liste récupérée.

Ce type de test est particulièrement intéressant car il reproduit une situation proche de la réalité. Nous ne vérifions plus seulement qu'une route fonctionne isolément. Nous vérifions qu'un ensemble de routes collabore correctement pour permettre à un utilisateur d'accomplir son objectif.
Les tests d'intégration constituent justement ce pont entre les tests unitaires et les futurs tests end-to-end que nous découvrirons plus tard.
Comme pour les tests unitaires, les échecs font partie du processus normal de développement. La différence est qu'un test d'intégration implique davantage de composants, ce qui multiplie les causes possibles d'erreur.
Dans notre application fil rouge, quatre situations reviennent fréquemment :
La première concerne le code de statut HTTP. Un test peut attendre un code200alors que la route retourne un401. Dans ce cas, il faut vérifier les données envoyées dans la requête ainsi que la logique d'authentification.
La deuxième situation concerne la structure de la réponse. Le test attend peut-être un champ nommétoken, mais la route retourne un champ nomméjwt. Le problème ne vient alors pas nécessairement du fonctionnement de la route, mais d'une différence entre ce qui est attendu et ce qui est réellement fourni.
La troisième cause fréquente concerne les données préparées dansbeforeEach(). Si vous oubliez d'appelerresetStore()avant le scénario, l'état du store peut varier d'un test à l'autre et les tests de connexion deviennent imprévisibles, même si le handler fonctionne parfaitement.
Enfin, les erreurs d'import sont très courantes lors des premières manipulations. Un chemin incorrect ou un export manquant empêchera tout simplement Vitest d'exécuter le test.
Face à un échec, gardez le même réflexe que dans le chapitre précédent : commencez toujours par lire le message affiché dans le terminal. Vitest indique généralement la ligne concernée, la valeur attendue et la valeur réellement reçue. Ces informations suffisent souvent à identifier rapidement l'origine du problème.

Après avoir écrit les premiers tests unitaires du service d'authentification dans le chapitre précédent, votre tech lead souhaite désormais sécuriser les routes principales de l'application. L'objectif est de garantir qu'un utilisateur peut se connecter, obtenir un jeton valide, créer une tâche puis consulter sa liste de tâches.
Pour cela, vous allez écrire vos premiers tests d'intégration back-end en utilisant directement les route handlers Next.js.
Écrivez un test pour la routePOST /api/auth/login.
Couvrez le cas de succès et le cas d'échec.
Préparez un environnement de test reproductible avecbeforeEach().
Réinitialisez le store avecresetStore()plutôt que de créer ou supprimer des données manuellement.
Écrivez un scénario completPOST /api/tasks → GET /api/tasks.
Passez le jeton JWT dans l'en-têteAuthorization.
Provoquez volontairement un échec et identifiez sa cause à partir du message affiché par Vitest.
Un test d'intégration back vérifie le comportement complet d'une route API : code HTTP, structure de réponse et logique métier.
Les route handlers Next.js peuvent être appelés directement dans Vitest grâce aux objets standardsRequestetResponse.
La fonctionbeforeEach()associée àresetStore()garantit l'isolation des tests en réinitialisant les données avant chaque scénario.
Les tests d'intégration permettent de reproduire des parcours métier réels impliquant plusieurs routes qui collaborent entre elles.
Les messages d'échec Vitest fournissent les informations nécessaires pour localiser rapidement l'origine d'un problème.
Vous savez maintenant tester les routes API de votre application. Dans le prochain chapitre, vous découvrirez comment tester les composants de l'interface utilisateur afin de sécuriser également la partie front-end du projet.