Tester les scénarios de production
Objectif : Vérifier les réponses HTTP, les échecs de tâche, les accès refusés et la génération d’export.
En production, une route peut répondre correctement dans le cas nominal tout en échouant lorsqu’une dépendance tombe en panne. Vous allez construire des tests qui vérifient les comportements réellement importants du service de commandes, sans solliciter une base de données ni une file de tâches réelles.
Vous avez déjà assemblé la facturation asynchrone et l’export protégé en streaming. Cette leçon ne redéfinit pas leurs interfaces : le contrat du projet ne précise pas les signatures exactes des fabriques Express existantes, donc raccordez le point de création de l’application ci-dessous à la fabrique de src/app.js et aux dépendances réellement utilisées.
Définir des scénarios observables
Un test d’intégration traverse plusieurs composants, par exemple une route, ses middlewares et le service appelé, tout en remplaçant les dépendances externes. Une dépendance simulée répond de façon contrôlée : vous pouvez faire réussir ou échouer le producteur de tâches, accorder ou refuser un accès, et fournir un export déterministe. Le test vérifie alors le contrat observable par le client, plutôt que les détails internes d’une bibliothèque.
- Publication réussie : la commande est persistée, la tâche est acceptée et la réponse HTTP respecte le contrat de la route.
- Échec de publication : le client reçoit une erreur maîtrisée ; le journal identifie l’échec sans exposer de secret ni de détail interne.
- Accès refusé : un appel sans identité valide ou sans permission n’ouvre pas le flux d’export.
- Export réussi : le statut, le type de contenu et les octets reçus correspondent à un CSV connu.
Organiser les tests autour de l’injection
Placez les scénarios dans le fichier prévu, tests/commandes.test.js, et utilisez le lanceur de tests déjà retenu par le projet. Le code suivant montre la structure avec l’API intégrée de Node.js : le point à adapter est volontairement limité à l’import et à l’appel de la fabrique existante. Fournissez-lui les doubles de dépendances conformément aux signatures déjà présentes ; ne créez pas une seconde application de production dans le test.
const { test, before, after } = require('node:test');
const assert = require('node:assert/strict');
const http = require('node:http');
// Raccordez cet import à l’export réel de src/app.js.
const { creerApplication } = require('../src/app');
test('les scénarios de production du service de commandes', async (t) => {
const appels = { publier: 0, creerExport: 0 };
const evenements = [];
const dependances = {
commandes: {
enregistrer: async (commande) => ({ id: 'cmd-42', ...commande })
},
factures: {
publier: async () => {
appels.publier += 1;
return { acceptee: true };
}
},
authentification: {
verifier: async (jeton) => jeton === 'jeton-test'
? { id: 'utilisateur-7', permissions: ['commandes:exporter'] }
: null
},
limitation: { autoriser: async () => true },
exports: {
creerFlux: async () => {
appels.creerExport += 1;
return require('node:stream').Readable.from([
'id,total\ncmd-42,125.00\n'
]);
}
},
journal: {
info: (evenement, donnees) => evenements.push({ niveau: 'info', evenement, donnees }),
error: (evenement, donnees) => evenements.push({ niveau: 'error', evenement, donnees })
}
};
// La fabrique doit retourner une application HTTP testable.
const application = creerApplication(dependances);
const serveur = http.createServer(application);
await new Promise((resolve) => serveur.listen(0, '127.0.0.1', resolve));
const adresse = serveur.address();
const base = `http://127.0.0.1:${adresse.port}`;
after(() => new Promise((resolve, reject) =>
serveur.close((erreur) => erreur ? reject(erreur) : resolve())
));
await t.test('refuse un export sans authentification avant de créer le flux', async () => {
const reponse = await fetch(`${base}/commandes/export`);
assert.equal(reponse.status, 401);
assert.equal(appels.creerExport, 0);
});
await t.test('renvoie le CSV pour un utilisateur autorisé', async () => {
const reponse = await fetch(`${base}/commandes/export`, {
headers: { authorization: 'Bearer jeton-test' }
});
assert.equal(reponse.status, 200);
assert.match(reponse.headers.get('content-type'), /text\/csv/);
assert.equal(await reponse.text(), 'id,total\ncmd-42,125.00\n');
assert.equal(appels.creerExport, 1);
});
assert.equal(appels.publier, 0);
assert.ok(Array.isArray(evenements));
});L’exemple vérifie deux propriétés utiles : l’accès est refusé avant l’ouverture de l’export et le CSV reçu est comparé octet pour octet à une petite valeur connue. Il reste à ajouter, avec les véritables routes et signatures du projet, les cas d’enregistrement de commande, de publication échouée, de limitation dépassée et de permission insuffisante. Si les réponses du service masquent volontairement le détail d’une panne, testez ce statut et ce message public, puis inspectez séparément l’événement de journal attendu.
Vérifier les journaux et maîtriser les dépendances
Les tests ne doivent ni appeler une vraie file ni nécessiter des secrets de production. Injectez un producteur simulé qui rejette sa promesse pour le scénario d’échec, puis vérifiez que l’API répond selon son contrat et que le journal reçoit un événement de niveau erreur avec les champs utiles. Faites aussi un test où le dépôt échoue avant publication : le compteur d’appels du producteur doit rester à zéro, ce qui confirme l’ordre persistance-publication défini dans les leçons précédentes.
Pour contrôler les journaux, fournissez un collecteur en mémoire à la place du journal de production et inspectez les événements après la requête. Vérifiez la présence de l’identifiant de corrélation si le middleware le transmet, mais n’attachez pas le test à l’ordre exact des propriétés JSON. N’enregistrez pas le jeton utilisé dans les assertions : un test doit également détecter une fuite de secret dans les données journalisées.
À retenir
- Couvrez les succès et les échecs observables des routes, pas seulement le chemin nominal.
- Simulez file, dépôt, identité, export et journal par injection ; ne dépendez pas d’infrastructures de production.
- Vérifiez qu’un refus d’accès intervient avant la création d’un flux d’export.
- Contrôlez séparément la réponse HTTP, les appels aux dépendances et les événements journalisés.
- Adaptez le raccordement à la fabrique et aux signatures réelles sans inventer de nouvelle structure.
La prochaine leçon transforme ces vérifications en préparation de mise en service et en procédure de retour arrière.