测试消耗承诺的功能

时间:2015-11-17 18:00:37

标签: node.js testing jasmine promise bluebird

我有一个MEAN应用程序,我试图让测试在节点方面工作。异步事件包含在promises中,它们在控制器中使用。我在测试控制器时失败了:(

控制器我试图测试:

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);
  });
};

subjectRepository是我们的数据来源,它会返回一个承诺(mpromise,因为我们正在使用mongoose,但它并不重要):

因此,在我们的测试中,我们尝试模拟请求(我们将依赖注入容器从中间件注入req)和响应(如果调用了response.json(),则测试成功我们试图获取的主题)和subjectRepository。我们使用bluebird(虽然我是出于挫败感尝试了其他人)来为我们的模拟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();
  });
});

问题:如何在承诺成功后让测试运行expect()

节点v0.12

GitHub上的代码适用于任何有兴趣的人:https://github.com/mihaeu/fair-projects

也许我应该提一下从路由器调用控制器:

// router handles only routing
// and controller handles data between view and model (=MVC)
subjectRouter.get('/:subjectId', subjectController.get);

我通过改变我们的控制器来交付承诺来实现这一目标,但我不确定这是我们想要的。难道没有办法让我的方法起作用吗?

  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
  }

1 个答案:

答案 0 :(得分:1)

您的代码错误地将业务逻辑与前向路由混合在一起。

如果你的getAll 触摸了请求和响应对象,它将看起来像这样:

ProjectController.prototype.getAll = function(subjectId) {
  return req.dic.subjectRepository.getById(subjectId).then(function(subject){
    return subject.projects;
  });
};

现在,它不再与请求响应生命周期或逻辑负责相关,测试它是微不足道的:

it("does foo", function(){
  // resolve to pass the test, reject otherwise, mocha or jasmine-as-promised
  return controller.getAll(152).then(...) 
});

这将使您的实际处理程序看起来像:

 app.get("/projects", function(req, res){
    controller.getAll(req.params.subjectId).then(function(result){
       res.json(result); 
    }, function(){
       res.status(404).send("...");
    });
 });