Getting a UnhandledPromiseRejectionWarning when testing using mocha/chai
The issue is caused by this:
.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
If the assertion fails, it will throw an error. This error will cause done()
never to get called, because the code errored out before it. That's what causes the timeout.
The "Unhandled promise rejection" is also caused by the failed assertion, because if an error is thrown in a catch()
handler, and there isn't a subsequent catch()
handler, the error will get swallowed (as explained in this article). The UnhandledPromiseRejectionWarning
warning is alerting you to this fact.
In general, if you want to test promise-based code in Mocha, you should rely on the fact that Mocha itself can handle promises already. You shouldn't use done()
, but instead, return a promise from your test. Mocha will then catch any errors itself.
Like this:
it('should transition with the correct event', () => {
...
return new Promise((resolve, reject) => {
...
}).then((state) => {
assert(state.action === 'DONE', 'should change state');
})
.catch((error) => {
assert.isNotOk(error,'Promise error');
});
});
UnhandledPromiseRejectionWarning during testing with mocha-chai using chai-http
You have two problems:
Your test doesn't fail despite the assertion error you get
This is because in line 14 you are missing a return
. Since the function you register in line 12 doesn't return anything, the promise resolves. As a result, the promise you return in line 8 also resolves and your test doesn't fail although it should.
Since you do not return the rejected promise the error isn't propagated. That's why you get the error message.
The assertion error itself
You are creating a resource and then requesting this resource assuming that its id is 2. You are getting a 404 response, which means that either the resource wasn't created at all or it has a different id.
When creating the resource you assign it an id in some way (it looks like you're just using a counter for this, but I can't tell for sure without seeing your implementation). A POST response should provide some information about this id. Ideally, it sends the id as the Location header, but often the complete created resource is sent as the response body. So before making the GET request, you need to extract the id from the POST response.
REST API test with Mocha & Chai- Unhandled promise rejection
The test passes and logs the "Unhandled promise rejection"
warning because the error is thrown within a Promise
which just causes the Promise
to reject.
Since the Promise
is not returned or await
-ed, the test completes successfully...
...and because nothing is waiting for or handling the rejected Promise
, Node.js logs a warning.
Details
An error thrown in a test will fail the test:
it('will fail', function () {
throw new Error('the error');
});
...but a rejected Promise
will not fail the test if it is not returned or await
-ed:
it('will pass with "Unhandled promise rejection."', function() {
Promise.reject(new Error('the error'));
});
.catch
returns a Promise
so throwing in a .catch
returns a rejected Promise
, but if that Promise
is not returned or await
-ed then the test will also pass:
it('will also pass with "Unhandled promise rejection."', function() {
Promise.reject(new Error('the error')).catch(err => { throw err });
});
...but if a rejected Promise
is returned or await
-ed then the test will fail:
it('will fail', async function() {
await Promise.reject(new Error('the error'));
});
And if you can show me the syntax of properly using async await for writing these tests please do that too.
For your test you can simplify it to this:
const chai = require('chai');
const expect = chai.expect;
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
describe('Students', async function() {
describe('/GET students', function() {
it('should GET all the students', async function() {
const res = await chai.request('http://localhost:5000').get('/students');
expect(res).to.equal('hola'); // <= this will fail as expected
});
});
});
Details
From the chai-http
doc:
If
Promise
is available,request()
becomes a Promise capable library
...so chai.request(...).get(...)
returns a Promise
.
You can simply await
the Promise
and the test will wait until the Promise
resolves before continuing.
Getting a UnhandledPromiseRejectionWarning when testing using nodejs
First you have an error when sending the response:
res.status(200).redirect("/");
Note: x
-> c
Then you have no condition to check if bcrypt.compare
causes an error.
Try the following code:
exports.login = async(req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).render('login', {
message: "PLEASE PROVIDE AN EMAIL AND/OR PASSWORD"
})
}
db.query('SELECT * FROM users WHERE email = ?', [email], async(error, results) => {
console.log(results);
try {
if (!results || !(await bcrypt.compare(password, results[0].password))) {
res.status(401).render('login', {
message: 'EMAIL OR PASSWORD IS INCORRECT'
})
} else {
const id = results[0].id;
const token = jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN
});
console.log("THE TOKEN IS " + token);
const cookieOptions = {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES * 24 * 60 * 60 * 1000
),
httpOnly: true
}
res.cookie('jwt', token, cookieOptions);
res.status(200).redirect("/");
}
} catch (error) {
console.log(error);
}
})
}
Handle promise rejection with Chai
For testing promise-based functions (async) use chai-as-promised plugin:
expect(validate.user(undefined, 'pass')).to.be.rejectedWith(Error, 'User does not exist');
In this case you do not put () => validate.user()
into expect
because it will not return a promise. async
functions always return a promise, so it's enough to just call your method inside expect
(without wrapping it into a method).
I hope this is clear :) If you have any questions - feel free to ask.
Related Topics
Chrome Extension: Make It Run Every Page Load
Map Isn't Showing on Google Maps JavaScript API V3 When Nested in a Div Tag
Convert String Array Representation Back to an Array
Case Insensitive Jquery Attribute Selector
Nested Element (Web Component) Can't Get Its Template
How to Call a Parent Method from Child Class in JavaScript
Jquery Add Required to Input Fields
Dom Refresh on Long Running Function
Deep Copy an Array in Angular 2 + Typescript
Date Parsing in JavaScript Is Different Between Safari and Chrome
Angular2 Component's "This" Is Undefined When Executing Callback Function
Array Like Objects in JavaScript
What Is Property in Hasownproperty in JavaScript
How to Create a Custom Error in JavaScript