web-dev-qa-db-fra.com

Comment se moquer des requêtes et des réponses dans nodejs pour tester les middleware / contrôleurs?

Mon application comporte plusieurs couches: middleware, contrôleurs, gestionnaires. L'interface des contrôleurs est identique à celle des middlewares: (req, res, next).

Ma question est donc: comment puis-je tester mes contrôleurs sans démarrer le serveur et envoyer de "vraies" demandes à localhost. Ce que je veux faire est de créer une requête, des instances de réponse comme le fait nodejs, puis d'appeler simplement la méthode des contrôleurs.

Quelque chose comme ça:

var req = new Request()
var res = new Response()
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)

Tout conseil est fortement apprécié. Merci!

P.S. la raison pour laquelle je veux le faire est qu'à la fin je voudrais tester si l'objet de réponse contient des variables correctes pour les vues jade.

32
potomok

Il y a une implémentation semi décente à node-mocks-http

Exiger:

var mocks = require('node-mocks-http');

vous pouvez ensuite composer des objets req et response:

req = mocks.createRequest();
res = mocks.createResponse();

Vous pouvez ensuite tester directement votre contrôleur:

var demoController = require('demoController');
demoController.login(req, res);

assert.equal(res.json, {})

mise en garde

Il existe au moment de la rédaction d'un problème dans cette implémentation lié au fait que l'émetteur d'événement n'est pas déclenché.

18
superluminary

Étant donné que JavaScript est un langage typé dynamiquement, vous pouvez créer des objets fictifs et les transmettre à vos contrôleurs comme suit:

var req = {};
var res = {};
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)

Si votre contrôleur a besoin d'une donnée ou d'une fonctionnalité particulière de votre objet de demande ou de réponse, vous devrez fournir ces données ou fonctionnalités dans vos simulations. Par exemple,

var req = {};
req.url = "http://google.com"; // fake the Url

var res = {};
res.write = function(chunk, encoding) { 
  // fake the write method 
};

var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
9
Hector Correa

J'essaierais d'utiliser dupertest pour cela. C'est un module de nœud que j'ai créé dans le but même de tester facilement le contrôleur sans avoir à faire tourner un nouveau serveur.

Il conserve la syntaxe familière des modules de nœuds comme request ou supertest, mais encore une fois, sans avoir besoin de faire tourner un serveur.

Il fonctionne un peu comme Hector l'a suggéré ci-dessus, mais s'intègre à un cadre de test comme Jasmine pour se sentir un peu plus transparent.

Un exemple relatif à votre question peut ressembler à:

request(controller.get_user)
  .params({id: user_id})
  .expect(user, done);

Ou la version longue plus explicite:

request(controller.get_user)
  .params({id: user_id})
  .end(function(response) {
    expect(response).toEqual(user);
    done();
  });

Remarque: les exemples supposent user_id et user sont définis quelque part, et que le contrôleur récupère et renvoie un utilisateur en fonction de l'id.

Edit: en lisant votre réponse à une réponse ci-dessus, je reconnais que l'inconvénient est que ce module n'intègre pas par défaut une requête factice ou un objet de réponse plus robuste. dupertest facilite l'extension et l'ajout de propriétés aux deux req et res, mais par défaut, elles sont assez nues.

6
tydotg

Un post un peu ancien, mais je voudrais donner mes 2 cents. L'approche que vous souhaitez adopter varie selon que vous effectuez des tests unitaires ou des tests d'intégration. Si vous suivez la voie de l'utilisation de supertest, cela signifie que vous exécutez le code d'implémentation réel et que vous effectuez des tests d'intégration. Si c'est ce que vous voulez faire, cette approche est très bien.

Mais si vous faites des tests unitaires, vous vous moquerez des objets req et res (et de toute autre dépendance impliquée). Dans le code ci-dessous (code non pertinent supprimé par souci de concision), je me moque de res et ne donne qu'une implémentation simulée de la méthode json, car c'est la seule méthode dont j'ai besoin pour mes tests.

// SUT
kids.index = function (req, res) {
if (!req.user || !req.user._id) {
    res.json({
        err: "Invalid request."
    });
} else {
    // non-relevent code
}
};

// Unit test
var req, res, err, sentData;
describe('index', function () {

    beforeEach(function () {
        res = {
            json: function (resp) {
                err = resp.err;
                sentData = resp.kids;
            }
        };
    });
    it("should return error if no user passed in request", function () {
        req = {};

        kidsController.index(req, res);
        expect(err).to.equal("Invalid request.");

    });

    /// More tests....
})
4
Abhishek Jain

Si vous souhaitez utiliser les vrais objets req et res, vous devez envoyer de vraies requêtes au serveur. Cependant, c'est beaucoup plus facile que vous ne le pensez. Il y a beaucoup d'exemples à l'express github repo . Ce qui suit montre les tests pour req.route

var express = require('../')
  , request = require('./support/http');

describe('req', function(){
  describe('.route', function(){
    it('should be the executed Route', function(done){
      var app = express();

      app.get('/user/:id/edit', function(req, res){

        // test your controllers with req,res here (like below)

        req.route.method.should.equal('get');
        req.route.path.should.equal('/user/:id/edit');
        res.end();
      });

      request(app)
      .get('/user/12/edit')
      .expect(200, done);
    })
  })
})
4
zemirco