单元测试结束承诺链的函数

Unit test a function that ends the promise chain

本文关键字:函数 承诺 结束 单元测试      更新时间:2023-09-26

假设我在一个名为UserController的类中有一个函数,它沿着这些行做一些事情(其中userService.createUser()返回一个promise):

function createUser(req, res)
{
  const userInfo = req.body;
  userService.createUser(userInfo)
    .then(function(){res.json({message: "User added successfully"})})
    .fail(function(error){res.send(error)})
    .done();
}

我如何测试当promise解析时,调用res.json(),而当promise拒绝时,则调用res.send(error)

我试过写这样一个测试:

const userService = ...
const userController = new UserController(userService);
const response = {send: sinon.stub()};
...
const anError = new Error();
userService.createUser = sinon.stub().returns(Q.reject(anError));
userController.createUser(request, response);
expect(response.send).to.be.calledWith(anError);

但测试失败,并显示"response.send从不调用"。在调用res.send(error)之前,我也尝试过记录一些内容,但记录确实发生了。

我的猜测是expect()是在执行res.send(error)之前调用的,因为它是异步的。

我对promise和单元测试还很陌生,这是我的体系结构还是我对promises的使用?

我用Q表示承诺,用mocha、chai、sinon表示单元测试。

当您进行异步调用时,expect语句会在userController.createUser()行之后立即调用。因此,当评估断言时,它还没有被调用。

要异步测试代码,您需要在it语句中声明done,然后手动调用它以获得结果。

在您的测试文件上:

it('should work', function(done) {
  ...
  userController.createUser(request, response);
  process.nextTick(function(){
    expect(response.send).to.be.calledWith(anError);
    done();
  });
});  

这将使Mocha(我假设您正在使用它)在调用done()时评估您的excpect

或者,您可以在UserController.createUser函数上设置cb函数,并在.done():上调用它

用户控制器

function createUser(req, res, cb) {
  const userInfo = req.body;
  userService.createUser(userInfo)
    .then(function(){res.json({message: "User added successfully"})})
    .fail(function(error){res.send(error)})
    .done(function(){ if(cb) cb() });
  }

然后在你的测试中:

userController.createUser(request, response, function() {
  expect(response.send).to.be.calledWith(anError);
  done();
});

假设您使用Mocha或Jasmine作为框架,更简单的方法是按照刚开始的方式进行,但只需完全跳过Sinon(因为这里不需要它,除非您测试收到的实际参数):

// observe the `done` callback - calling it signals success
it('should call send on successful service calls', (done) => {
  // assuming  same code as in question
  ... 
  const response = {send: done};
  userController.createUser(request, response);
});

// observe the `done` callback - calling it signals success
it('should call send on failing service calls', (done) => {
  // assuming  same code as in question
  ... 
  const response = {send: err => err? done(): done(new Error("No error received"))};
  userController.createUser(request, response);
});

披露:我是Sinon维护团队的一员。