【问题标题】:Testing a function which consumes promises测试一个使用 Promise 的函数
【发布时间】:2015-11-17 18:00:37
【问题描述】:

我有一个 MEAN 应用程序,我正在尝试让测试在节点端工作。异步事件包装在承诺中,在控制器中使用。我在测试控制器时失败了:(

我要测试的控制器:

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() 已被我们尝试获取的主题调用,则测试成功) 和我们的主题存储库。我们使用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
  }

【问题讨论】:

  • 非常困难,因为您的 getAll 方法不会返回承诺。如果确实如此(就像在您的更新中一样),那应该很容易。

标签: node.js testing jasmine promise bluebird


【解决方案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("...");
    });
 });

【讨论】:

  • 感谢本杰明的快速回复。这样一来,您就使 Controller 成为了模型,并且它简单地传递了完全相同的承诺。我的下一个问题是如何测试路由中的回调。在 MVC 中,控制器的工作是充当视图和模型之间的“控制器”。 // 我们的路由器将存储库注入到控制器中,控制器 // 是您发布的提取回调(回调更难测试 // 并且路由将与控制逻辑混合) subjectRouter.get('/', subjectController.getAll );
猜你喜欢
  • 2021-06-23
  • 1970-01-01
  • 1970-01-01
  • 2016-10-07
  • 1970-01-01
  • 2016-09-03
  • 2017-07-31
  • 2018-04-10
  • 2017-03-26
相关资源
最近更新 更多