Comment une unité teste-t-elle les routes avec Express?

Je suis en train d’apprendre Node.js et je joue avec Express . Vraiment, comme le cadre, mais j’ai du mal à comprendre comment écrire un test d’unité / d’intégration pour un itinéraire.

Être capable de tester des modules simples est facile et cela a été fait avec Mocha ; Cependant, mes tests unitaires avec Express échouent car l’object de réponse que je transmets ne conserve pas les valeurs.

Route-Function Under Test (routes / index.js):

exports.index = function(req, res){ res.render('index', { title: 'Express' }) }; 

Module de test d’unité:

 var should = require("should") , routes = require("../routes"); var request = {}; var response = { viewName: "" , data : {} , render: function(view, viewData) { viewName = view; data = viewData; } }; describe("Routing", function(){ describe("Default Route", function(){ it("should provide the a title and the index view name", function(){ routes.index(request, response); response.viewName.should.equal("index"); }); }); }); 

Lorsque j’exécute ceci, il échoue pour “Erreur: fuites globales détectées: viewName, data”.

  1. Où est-ce que je me trompe pour que je puisse faire fonctionner ça?

  2. Y a-t-il un meilleur moyen pour moi de tester mon code à ce niveau?

Mise à jour 1. Extrait de code corrigé depuis que j’ai initialement oublié “it ()”.

Changez votre object de réponse:

 var response = { viewName: "" , data : {} , render: function(view, viewData) { this.viewName = view; this.data = viewData; } }; 

Et ça va marcher.

Comme d’autres l’ont recommandé dans les commentaires, il semble que la manière canonique de tester les contrôleurs Express se fasse par le biais du plus puissant .

Un exemple de test pourrait ressembler à ceci:

 describe('GET /users', function(){ it('respond with json', function(done){ request(app) .get('/users') .set('Accept', 'application/json') .expect(200) .end(function(err, res){ if (err) return done(err); done() }); }) }); 

Upside: vous pouvez tester toute votre stack en une seule fois.

Inconvénient: il se sent et agit un peu comme les tests d’intégration.

Le moyen le plus simple de tester HTTP avec express consiste à voler l’aide http de TJ

J’utilise personnellement son assistant

 it("should do something", function (done) { request(app()) .get('/session/new') .expect('GET', done) }) 

Si vous voulez tester spécifiquement votre object routes, passez les correctifs corrects

 describe("Default Route", function(){ it("should provide the a title and the index view name", function(done){ routes.index({}, { render: function (viewName) { viewName.should.equal("index") done() } }) }) }) 

Je suis arrivé à la conclusion que la seule façon de tester les applications express de manière unitaire est de maintenir une grande séparation entre les gestionnaires de requêtes et votre logique de base.

Ainsi, la logique de votre application doit se trouver dans des modules distincts pouvant être require et testés par l’unité, et dépendre au minimum des classes Express Request et Response.

Ensuite, dans les gestionnaires de requêtes, vous devez appeler les méthodes appropriées de vos classes logiques de base.

Je vais mettre un exemple une fois que j’ai fini de restructurer mon application actuelle!

Je suppose que quelque chose comme ça? (N’hésitez pas à fourrer le gros ou le commentaire, je suis encore en train de l’explorer).

modifier

Voici un petit exemple, en ligne. Voir l’essentiel pour un exemple plus détaillé.

 /// usercontroller.js var UserController = { _database: null, setDatabase: function(db) { this._database = db; }, findUserByEmail: function(email, callback) { this._database.collection('usercollection').findOne({ email: email }, callback); } }; module.exports = UserController; /// routes.js /* GET user by email */ router.get('/:email', function(req, res) { var UserController = require('./usercontroller'); UserController.setDB(databaseHandleFromSomewhere); UserController.findUserByEmail(req.params.email, function(err, result) { if (err) throw err; res.json(result); }); }); 

si les tests unitaires avec express 4 notent cet exemple de gjohnson :

 var express = require('express'); var request = require('supertest'); var app = express(); var router = express.Router(); router.get('/user', function(req, res){ res.send(200, { name: 'tobi' }); }); app.use(router); request(app) .get('/user') .expect('Content-Type', /json/) .expect('Content-Length', '15') .expect(200) .end(function(err, res){ if (err) throw err; }); 

Pour réaliser les tests unitaires au lieu des tests d’intégration, j’ai simulé l’object de réponse du gestionnaire de requêtes.

 /* app.js */ import endpointHandler from './endpointHandler'; // ... app.post('/endpoint', endpointHandler); // ... /* endpointHandler.js */ const endpointHandler = (req, res) => { try { const { username, location } = req.body; if (!(username && location)) { throw ({ status: 400, message: 'Missing parameters' }); } res.status(200).json({ location, user, message: 'Thanks for sharing your location with me.', }); } catch (error) { console.error(error); res.status(error.status).send(error.message); } }; export default endpointHandler; /* response.mock.js */ import { EventEmitter } from 'events'; class Response extends EventEmitter { private resStatus; json(response, status) { this.send(response, status); } send(response, status) { this.emit('response', { response, status: this.resStatus || status, }); } status(status) { this.resStatus = status; return this; } } export default Response; /* endpointHandler.test.js */ import Response from './response.mock'; import endpointHandler from './endpointHander'; describe('endpoint handler test suite', () => { it('should fail on empty body', (done) => { const res = new Response(); res.on('response', (response) => { expect(response.status).toBe(400); done(); }); endpointHandler({ body: {} }, res); }); }); 

Ensuite, pour effectuer le test d’intégration, vous pouvez simuler votre endpointHandler et appeler le noeud final avec supertest .

Je me demandais aussi ceci, mais spécifiquement pour les tests unitaires et non pour les tests d’intégration. C’est ce que je fais en ce moment,

 test('/api base path', function onTest(t) { t.plan(1); var path = routerObj.path; t.equals(path, '/api'); }); test('Subrouters loaded', function onTest(t) { t.plan(1); var router = routerObj.router; t.equals(router.stack.length, 5); }); 

Où routerObj est juste {router: expressRouter, path: '/api'} . Je charge ensuite les sous-routeurs avec var loginRouterInfo = require('./login')(express.Router({mergeParams: true})); et l’application express appelle ensuite une fonction d’initialisation prenant le routeur express comme paramètre. Le initRouter appelle ensuite router.use(loginRouterInfo.path, loginRouterInfo.router); monter le sous-routeur.

Le sous-routeur peut être testé avec:

 var test = require('tape'); var routerInit = require('../login'); var express = require('express'); var routerObj = routerInit(express.Router()); test('/login base path', function onTest(t) { t.plan(1); var path = routerObj.path; t.equals(path, '/login'); }); test('GET /', function onTest(t) { t.plan(2); var route = routerObj.router.stack[0].route; var routeGetMethod = route.methods.get; t.equals(routeGetMethod, true); var routePath = route.path; t.equals(routePath, '/'); });