sifrenette sifrenette -4 years ago 56
Javascript Question

Unit test a function that ends the promise chain

Let's say I have a function in a class named UserController that does something along those lines (where

userService.createUser()
returns a 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();
}


How can I test that, when the promise resolves,
res.json()
is called, and when the promise rejects,
res.send(error)
is called?

I have tried writing a test like this:

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


But the test fails with "response.send is never called". I also tried logging something before calling
res.send(error)
and the logging does happen.

My guess is that
expect()
is called before
res.send(error)
is executed since it's asynchronous.

I'm fairly new with promises and unit tests, is it something with my architecture or my use of promises?

I'm using Q for promises and mocha, chai, sinon for my unit tests.

Answer Source

As you have an asynchronous call the expect statement is called right after userController.createUser() line. So when the assertion is evaluated it has not been called yet.

To test your code asynchronously you will need to declare done on your it statement and then call it manually to get the result.

On your test file:

it('should work', function(done) {
  ...
  userController.createUser(request, response);

  process.nextTick(function(){
    expect(response.send).to.be.calledWith(anError);
    done();
  });
});  

This will make Mocha (I'm assuming you are using it) evaluate your excpect just when done() is called.

Alternatively you could set a cb function on your UserController.createUser function and call it on .done():

UserController

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

And then on your test:

userController.createUser(request, response, function() {
  expect(response.send).to.be.calledWith(anError);
  done();
});
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download