I'm using chai-as-promised to test some promises. My issue is I'm not sure how to have multiple expect statements in a single test. In order for the expect().to.be.fulfilled
to work properly, I need to return it, like this:
it('test', () => {
return expect(promise).to.be.fulfilled
}
... or to use notify
, like this:
it('test', (done) => {
expect(promise).to.be.fulfilled.notify(done)
}
The issue comes when I have another thing I need to check, such as that a certain function gets called, like this:
it('test', (done) => {
var promise = doSomething()
expect(sinon_function_spy.callCount).to.equal(1)
expect(promise).to.be.fulfilled.notify(done)
})
The problem here is that, because doSomething()
is asynchronous, the call to sinon_function_spy
may not have occurred yet when I call that expect
, making this test flaky. If I use a then
, like this:
it('test', (done) => {
var promise = doSomething()
promise.then(() => {
expect(sinon_function_spy.callCount).to.equal(1)
})
expect(promise).to.be.fulfilled.notify(done)
})
Then the test technically passes and fails as expected, but it will fail because the promise gets rejected, due to the thrown exception in the then
call. Similarly, if I have a case where the promise is expected to reject:
it('test', (done) => {
var promise = doSomething()
promise.then(() => {
expect(sinon_function_spy.callCount).to.equal(1)
})
expect(promise).to.be.rejected.notify(done)
})
Then the check on the sinon_function_spy
never gets called, since the promise was rejected and doesn't call then
.
How can I get both expect
statements to reliably execute and return the correct values?
If you're using mocha or jest as your test framework you can return the promise with expectations in your then()
block:
it('test', () => {
return doSomething().then( () => {
expect(sinon_function_spy.callCount).to.equal(1);
});
});
This test won't end until the promise successfully completes AND the expect
has been run. If you're using jasmine you can use the jasmine-promises
package to get the same functionality.
For the reverse case, I'd recommend creating a wrapper that reverse the polarity of the promise:
function reverse( promise ) {
//use a single then block to avoid issues with both callbacks triggering
return promise.then(
() => { throw new Error("Promise should not succeed"); }
e => e; //resolves the promise with the rejection error
);
}
Now you can do
it('test', () => {
return reverse( doSomethingWrong() ).then( error => {
expect( error.message ).to.equal("Oh no");
});
});
A way to achieve multiple expects
it('should fail if no auth', () => {
const promise = chai.request(server).get('/albums');
return expect(promise).to.be.rejected.then(() => {
return promise.catch(err => {
expect(err).not.to.be.null;
expect(err.response).to.have.status(401);
expect(err.response).to.be.a.json;
});
});
});
In the case of wanting to assert that the Promise is fulfilled and a call was performed as expected, you don't really need that first part as an assertion. The mocha test case itself will fail if the Promise rejects as long as you are returning it:
it('test', () => {
return doSomething()
.then(() => {
expect(sinon_function_spy.callCount).to.equal(1)
});
});
If the Promise returned by doSomething()
rejects, so will the test case. If the expect
assertion fails, it will also fail the test case with that failed assertion. If you want to be a bit more explicit:
it('test', () => {
return doSomething()
.then(() => {
expect(sinon_function_spy.callCount).to.equal(1)
}, err => {
expect(err).to.not.exist;
});
});
...you can catch the error. Note that with this flavor of then
with two callbacks, the assertion failing in the first callback will not reach the second callback, so it'll just be Mocha that sees the failed assertion.
Here's how you can do an expected failed Promise:
it('test', () => {
return doSomething()
.then(() => {
throw new Error('Promise should not have resolved');
}, err => {
expect(err).to.exist;
expect(sinon_function_spy.callCount).to.equal(1)
});
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With