Testing rejected promise in Mocha/Chai

cyberwombat picture cyberwombat · Aug 6, 2015 · Viewed 25.3k times · Source

I have a class that rejects a promise:

Sync.prototype.doCall = function(verb, method, data) {
  var self = this;

  self.client = P.promisifyAll(new Client());

  var res = this.queue.then(function() {
    return self.client.callAsync(verb, method, data)
      .then(function(res) {
        return;
      })
      .catch(function(err) {    
        // This is what gets called in my test    
        return P.reject('Boo');
      });
  });

  this.queue = res.delay(this.options.throttle * 1000);
  return res;
};

Sync.prototype.sendNote = function(data) {
  var self = this;
  return self.doCall('POST', '/Invoice', {
    Invoice: data
  }).then(function(res) {
    return data;
  });
};

In my test:

return expect(s.sendNote(data)).to.eventually.be.rejectedWith('Boo');

However while the test passes it throws the error to the console.

Unhandled rejection Error: Boo ...

With non promise errors I have used bind to test to prevent the error from being thrown until Chai could wrap and test:

return expect(s.sendNote.bind(s, data)).to.eventually.be.rejectedWith('Boo');

However this does not work with this and returns:

TypeError: [Function] is not a thenable.

What is the correct way to test for this?

Answer

fearless_fool picture fearless_fool · May 14, 2016

(Disclaimer: This is a good question even for people that do not use Bluebird. I've posted a similar answer here; this answer will work for people that aren't using Bluebird.)

with chai-as-promised

Here's how you can use chai-as-promised to test both resolve and reject cases for a Promise:

var chai = require('chai');
var expect = chai.expect;
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);

...

it('resolves as promised', function() {
    return expect(Promise.resolve('woof')).to.eventually.equal('woof');
});

it('rejects as promised', function() {
    return expect(Promise.reject('caw')).to.be.rejectedWith('caw');
});

without chai-as-promised

You can accomplish the same without chai-as-promised like this:

it('resolves as promised', function() {
  return Promise.resolve("woof")
    .then(function(m) { expect(m).to.equal('woof'); })
    .catch(function(e) { throw e })  // use error thrown by test suite
           ;
});

it('rejects as promised', function() {
    return Promise.reject("caw")
        .then(function(m) { throw new Error('was not supposed to succeed'); })
        .catch(function(m) { expect(m).to.equal('caw'); })
            ;
});