mardi 17 novembre 2015

Testing a function which consumes promises

long-time reader, first-time writer :P Thanks to you guys, developing is much more fun!

So my problem: I've got a MEAN app and I'm trying to get tests to work on the node side. Async events are wrapped in promises, which are consumed in the controller. I failed at testing the controller :(

The controller I'm trying to test:

ProjectController.prototype.getAll = function(req, res, next) {
  req.dic.subjectRepository
  .getById(req.params.subjectId)
  .then(function(subject) {
    res.json(subject.projects);
  }, function(err) {
    return res.status(404).send('Subject does not exist.' + err);
  });
};

The subjectRepository is our data source, which returns a promise (mpromise because under the hood we're using mongoose, but it shouldn't matter):

So in our test we tried mocking the request (we're injecting our dependency injection container from a middleware into the req) and response (the test succeeds if response.json() has been called with the subjects we tried to fetch) and our subjectRepository. We used bluebird (although I tried others out of frustration) to create fake promises for our mocked subjectRepository:

describe('SubjectController', function() {

  'use strict';

  var Promise = require('bluebird');

  it('gets all existing subjects', function() {

    // -------------------------------------
    // subjectRepository Mock
    var subjectRepository = {
      getAll: function() {},
    };
    var subjectPromise = Promise.resolve([
      {name: 'test'},
      {name: 'test2'},
    ]);
    spyOn(subjectRepository, 'getAll').andReturn(subjectPromise);

    // -------------------------------------
    // request mock
    var req = {
      dic: {
        subjectRepository: subjectRepository,
      },
    };

    // -------------------------------------
    // response mock
    var res = {
      json: function() {},
      send: function() {},
    };
    spyOn(res, 'json');

    // -------------------------------------
    // actual test
    var subjectController = new (require('../../../private/controllers/SubjectController'))();
    subjectController.getAll(req, res);

    // this succeeds
    expect(subjectRepository.getAll).toHaveBeenCalled();

    // this fails
    // expect(res.json).toHaveBeenCalled();
  });
});

Question: How do I make the test run the expect() AFTER the promise succeeded?

Node v0.12

Thanks so much in advance.

Yours, Michael

P.S.: Not a native speaker, so sorry if I fucked up. P.P.S.: The code is on GitHub for anyone who's interested: http://ift.tt/1SADQOf

UPDATE:

I got this to work, by changing our controllers to hand down the promises, but I'm not sure this is what we want. Isn't there a way to get my approach to work?

  it('gets all existing subjects', function(done) {    

      // ...

    var subjectController = new (require('../../../private/controllers/SubjectController'))();
    subjectController.getAll(req, res).then(function() {
      expect(res.json).toHaveBeenCalledWith(testSubjects);    // success
    }).finally(done);
    expect(subjectRepository.getAll).toHaveBeenCalled();    // success
  }

Aucun commentaire:

Enregistrer un commentaire