Chai rejectedWith doesn't chain properly

54 views Asked by At

I have read numerous posts and docs and cannot seem to figure out why this code doesn't work as I think it should.

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

async function foo(val) {

    if (!val) {
        const err = new Error();
        err.status = 404;
        throw err;
    }

    return new Promise(resolve => sleepSetTimeout_ctrl = setTimeout(resolve, 10));
}

describe('does not work', () => {
    it('throws', async () => {
        await expect(foo()).to.be.rejectedWith(Error).and.have.property('status');
    });
});

If I end the expect at rejectedWith(Error) it works fine, but trying to test that the property exists fails with: AssertionError: expected {} to have property 'status'

1

There are 1 answers

1
jonrsharpe On

This happens because the return value from rejectedWith is a PromisedAssertion - you're asserting on whether the promise(-like object) has a status property. I actually get a slightly more helpful message which makes this clearer:

AssertionError: expected Promise{…} to have property 'status'

You can also see that if you test for a property it does have, like then:

await expect(foo()).to.be.rejectedWith(Error).and.have.property('then');  // ✅

To assert on the actual error object, you need to use chai-as-promised's chainers again, e.g.:

await expect(foo()).to.be.rejectedWith(Error).and.eventually.have.property("status");  // ✅
                                              // ^---------^

As an alternative, particularly if you have multiple assertions to make on the resolved/rejected value, just chain using .then:

await expect(foo()).to.be.rejectedWith(Error).then((err) => {
  expect(err).to.have.property("status", 404);
}); // ✅