【问题标题】:Unit testing promise based code in node.js express route/controllernode.js express route/controller中基于promise的单元测试代码
【发布时间】:2015-04-07 02:38:52
【问题描述】:

最近我在我的 rest api express 应用程序中从使用回调切换到使用 promise。 但是我在使用承诺的异步行为对路由/控制器进行单元测试时遇到了麻烦。这是需要进行单元测试的示例代码。

var handler =  function (req, res, next) {
  var query = {}, 
  var options = {
    sort: { updatedAt: -1 },
    limit: 10
  };
  if (req.query.before) {
    query.updatedAt = { $lt: req.query.before };
  }
  // User.findAsync returns bluebird promise
  User.findAsync(query, null, options).then(function (user) {
    res.json(user);
  }).catch(function (e) {
    next(e);
  });
}
router.get('/api/users', handler);

我测试上述代码的方法是监视 req、next 和 User.findAsync 并检查是否使用正确的参数调用它们。但是由于 promise 的异步行为,我无法检查 res.json 或 next 是否被调用。

我尝试存根 findAsync 以返回已解决的承诺 (Promise.resolve(user))。但是回调仍然是异步执行的。

我不确定我是否在测试快速应用程序的正确轨道上。

有什么好的策略可以很好地分离测试这种代码?

我也听说过使用 supertest。 但对我来说,使用 supertest 从 http 端点进行测试感觉更像是集成测试,它不是单元测试,而且相当昂贵。

另外,总的来说,我想知道尝试用单元测试(模型、控制器、中间件等)覆盖所有代码是否是一种好习惯,以及这样做的好策略或技术是什么。或者如果用 super test 测试 http 端点就足够了。

【问题讨论】:

  • 你使用什么测试框架? Mocha 对异步测试有很好的支持(我认为它可以在没有太大困难的情况下使用 Promise)。也许你可以发布你的测试用例?
  • 我使用 mocha 作为 sinon 的测试框架。我对返回 Promise 的函数进行单元测试没有问题,但我的路由/控制器函数不返回 Promise 它只是调用 res.json 或 next 在 then 回调中。这就是我遇到困难的地方。

标签: node.js unit-testing express promise bluebird


【解决方案1】:

如果你被测试的方法没有返回一个 Promise,那么你就不能在 Mocha 中使用 Promise 语法。您可以像测试任何其他异步方法一样测试您的方法——使用done 作为it 的参数。假设我们要测试您的 handler 函数:

var handler =  function (req, res, next) {
  //...
  User.findAsync(query, null, options).then(function (user) {
    res.json(user);
  }).catch(function (e) {
    next(e);
  });
}

我们可以这样写一个测试:

describe("The handler", function(){
     it("calls res.json", function(done){ // note the done argument
         handler({query: {before: 5}, // mock request
                 {json: done} // res.json calls our `done` param of this function
                 function(){ throw new Error("error called"); });
     });
});

请注意,我们模拟了请求、响应和 next 处理程序。我们的模拟响应有一个 json 方法,可以让测试知道它已经完成(如果你想在其中进行断言,这可以是一个函数),如果调用 next ,我们会抛出信号,表明它不是应该发生的事情.

【讨论】: