I'm quite new to node and express.
And have been trying to write test code using mocha, chai and chai-http.
Here's the part of source code.
const mongoose = require('mongoose'),
User = require('../../models/user');
const mongoUrl = 'mongodb://xxxxxxxxxxx';
describe('/test', function() {
before('connect', function() {
return mongoose.createConnection(mongoUrl);
});
beforeEach(async function(done) {
try {
await User.remove({}); // <-- This doesn't work
chai.request('http://localhost:3000')
.post('/api/test')
.send(something)
.end((err, res) => {
if (err) return done(err);
done();
});
} catch (error) {
done(error);
}
});
});
And I get the following error with "npm test"(nyc mocha --timeout 10000 test/**/*.js).
Error: Timeout of 10000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
I confirmed the database connection works properly from log.
And seems I get the timeout error with await User.remove({}).
I've also tried different methods such as a User.save()
But, I got the same error.
Do I need to do something special with database model and connection?
This is all pretty simple.
To avoid the error you must not use both done and async/await in Mocha at the same time. Either use async/await and remove both done as function parameter and done() call. Or use done. Then remove both async/await. See the example tests below for each case.
Use try/catch with async/await as you would normally use it with synchronous code.
Following are the most basic Mocha tests with both async/await and done approaches testing the same basic HTTP server endpoint.
This is async/await approach.
it('with async/await', async function() {
const res = await chai.request(server)
.get('/')
.send();
assert.equal(res.status, 200);
});
This is done approach.
it('with done & callbacks', (done) => {
chai.request(server)
.get('/')
.end((err, res) => {
assert.equal(res.status, 200);
done();
});
});
See the full test file snippet.
For working example additionally spin basic Express server as the tests counterpart in src/app.js.
See Chai HTTP plugin docs for more info on what you can do with request testing.
This is it.
I had the same problem and have not found a way to get any promises that involve mongoose working with Mocha/Chai.
What may help you is doing what I did and putting your mongoose code in a script so you can run it with node <scriptfile>.js. You can use that to confirm it's working properly by itself. In my test, the mongoose operation finished in less than a second. You can also call that file from another (non-test related) to confirm it executes properly and returns a promise. You can see from my example how to make sure you close db properly. Partial example:
...
db.close();
return new Promise((resolve) => {
db.on('disconnected', () => {
console.log('***************************************Mongoose CONNECTION TERMINATED');
resolve('user ready');
});
});
...
You may also find some clues by looking at the following issues here and here.
The work around that I did after wasting too much time trying to figure out this crazy behavior was to perform my mongoose needs in a route. I wrap each request that needs to use it in the end block of the extra chai.request... or use async. Example:
describe('something', () => {
it('should do something and change it back', async () => {
try {
// change user password
let re1 = await chai.request(app)
.post('/users/edit')
.set('authorization', `Bearer ${token}`)
.send({
username: 'user#domain.com',
password: 'password6',
});
expect(re1.statusCode).to.equal(200);
// change password back since before hook not working
let re2 = await chai.request(app)
.post('/users/edit')
.set('authorization', `Bearer ${token}`)
.send({
username: 'user#domain.com',
password: 'password6',
passwordNew: 'password',
passwordConfirm: 'password',
});
expect(re2.statusCode).to.equal(200);
} catch (error) {
// error stuff here
}
});
Note that using the try/catch syntax above will cause test that should normally fail to show passing and the results will be caught in the catch block. If you want to avoid that, just remove the try/catch.
How did you implement ./models/user? await only works if User.remove() returns a promise, not if it expects a callback. I would add debug information to your User.remove() function to see where it gets stuck.
Related
i am using Jest to test my code.
What i want achieve is to test redirection from http to https. (if it exists if process.env.IS_PRODUCTION).
I don't know how to test it, how to mockup this and so on...
I've tried standard get reqest but don't know how to mockup environment varible or test it in different way
it('should redirect from http to https, (done) => {
request(server)
.get('/')
.expect(301)
.end((err, res) => {
if (err) return done(err);
expect(res.text).toBe('...')
return done();
});
}, 5000);
I expect to be able to test this redirection :)
You could use the node-mocks-http libary which allows you to simulate a request and response object.
Example:
const request = httpMocks.createRequest({
method: 'POST',
url: '/',
});
const response = httpMocks.createResponse();
middlewareThatHandlesRedirect(request, response);
I never worked with jest but I believe that you can check the response.location parameter once the middleware has been called
Preface: I'm not familiar with jest or express or node. But I have found it to be much easier to test explicit configuration (instantiating objects with explicit values) vs implicit configuration (environmental variables and implementation switches on them):
I'm not sure what request or server are but explicit approach might look like:
it('should redirect from http to https, (done) => {
const server = new Server({
redirect_http_to_https: true,
});
request(server)
.get('/')
.expect(301)
.end((err, res) => {
if (err) return done(err);
expect(res.text).toBe('...')
return done();
});
}, 5000);
This allows the test to explicitly configure server to the state it needs instead of mucking with the environment.
This approach also helps to keep process configuration at the top level of your application:
const server = new Server({
redirect_http_to_https: process.env.IS_PRODUCTION,
});
I'm new to unit testing in the Node world and am struggling with this: I've setup a after cb to delete the records I've added during my tests, however I keep getting an error Error: done() called multiple times every time I delete the record on the db. Here's my code:
after((done) => {
User.deleteOne({email: user_email}, function(err, result) {
if(err) console.log(err);
console.log(result);
done();
});
});
If I do anything else (like just console something within the after block, I get no error at all.
What am I doing wrong?
Try with async/await style.
after(async () => {
const deleteResult = await User.deleteOne({email: user_email});
console.log(deleteResult);
});
With async/await you don't need to execute done, because mocha automatically handle promises.
More here and here
I'm migrating an app from Sails.js v0.12 to v1.0, which includes upgrading mocha from ^3.5.0 to ^5.2.0. I assume that is the root of the issue, but I can't seem to find a fix.
Before, when a test failed, there would be a summary of the error at the end of the mocha reporter output: the specific assertion that failed, file name, line number, error message, etc. Now, the reporter is coloring the it block in red, but no additional details are show.
I've tried changing the reporter in mocha.opts, which works for the actual execution output, but nothing is enabling the summary at the end. What am I missing?
// ./test/integration/models/User.test.js
describe('User', () => {
describe('find()', () => {
it('should return an array of users', () => {
return User.find()
.then((users) => {
users.should.be.a('array');
true.should.be.false; // No problems if this is removed
});
});
});
});
In the console:
> node ./node_modules/mocha/bin/mocha test/lifecycle.test.js test/integration/**/*.test.js
√ OtherModel method() should do something: 17ms
1) User find() should return an array of users
'Done.'
PS C:\repos\myproject>
Turns out that Mocha is fine and my test definitions are fine, I had simply fixed a totally different migration issue incorrectly. Since version 4, mocha will no longer automatically kill itself when it thinks all tests are complete. This means that one of these two options must be used:
Add --exit to mocha.opts or to wherever the command is called
Run process.exit() in your test suite's JavaScript
I tried the second option by adding process.exit() to the after() block in my test bootstrap file. This is a bad idea and resulted in the confusing behavior above.
To solve my issue, I removed my changes to the after() block and added --exit to mocha.opts. Thanks to Dhruv Choudhary for pointing me in the right direction.
you can use done callback. The first strategy suggested in the mocha documentation is using the ‘done’ callback. This is an extra argument to the callback in the it . You call it after the last assertion in your test.
I have seen many people using done() method in wrong way. for example, look at below code,
describe('User', () => {
describe('find()', () => {
it('should return an array of users', (done) => {
return User.find()
.then((users) => {
users.should.be.a('array');
true.should.be.false; // No problems if this is removed
done();
});
});
});
});
The above test will work fine and show the test passing, But calling done() in the same then callback is a bad idea because The above code works well until your expectation fails, you might get error like
The above failure is not very useful . If we want to utilize the mocha’s error we shouldn’t call done() from the same then() callback. See the below test
describe('User', () => {
describe('find()', () => {
it('should return an array of users', (done) => {
return User.find()
.then((users) => {
users.should.be.a('array');
true.should.be.false;
})
.then(() => done(), done)
.catch((error) => {
done(error);
});
});
});
});
do not forget to wrap your error with catch block.
Now see the difference in the mocha’s failure message with actual and expected..
Please try adding done callback to your test case .
describe('User', () => {
describe('find()', () => {
it('should return an array of users', (done) => {
return User.find()
.then((users) => {
users.should.be.a('array');
true.should.be.false; // No problems if this is removed
done();
});
});
});
});
I am writing a unit test to test my postgres schema. I am using node-pg, mocha, sinon, and chai.
This works - the test passes with no issues:
describe('When adding a user', ()=> {
it('should reject since email is used somewhere else', (done)=> {
pool.query(`INSERT INTO users(email, id, token)
VALUES($1, $2, $3)`, ['foo#email.com', '12346', 'fooToken'])
.then((result)=> {
console.log('nothing in here runs, you will not see this');
done()
})
.catch((result) => {
result.constraint.should.have.string('email_already_exists');
done();
})
})
});
But to make sure I am not getting a false positive, I change the assert to result.constraint.should.not.have.string('email_already_exists'); to purposely make the test fail.
Instead of the test failing, I get Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test..
What am I getting this?
If you would still like to use Promises for this, the problem is that unhandled exceptions in Promises are unfortunately not propagated but rather are silently ignored. As a result, no one calls Mocha's done method, leading to the timeout.
Attaching a listener to Node's unhandledRejection event as documented here should demonstrate this.
If you modify your original code and add a call to the Promise's done method (this isn't Mocha's done method!), then you'll be able to catch all errors and pass them to Mocha's done method:
it('tests something', done => {
pool.query('...')
.then(result => {
// ...
})
.catch(result => {
// assertion that fails
})
.done(result => {}, error => { done(error); });
});
Note that Promise.done() isn't (yet) part of the standard, but is nonetheless supported by many implementations. See for example here.
Answer:
The promise chain for node-pg causes this strange issue during testing. If I work off of callback, then no issue:
describe('When adding a user', ()=> {
it('should reject since email is used somewhere else', (done)=> {
function callback(err, result) {
err.constraint.should.not.have.string('email_already_exists');
done();
}
pool.query(`INSERT INTO users(email, id, token)
VALUES($1, $2, $3)`, ['foo#email.com', '12346', 'fooToken'], callback)
})
});
In my node application I'm using mocha to test my code. While calling many asynchronous functions using mocha, I'm getting timeout error (Error: timeout of 2000ms exceeded.). How can I resolve this?
var module = require('../lib/myModule');
var should = require('chai').should();
describe('Testing Module', function() {
it('Save Data', function(done) {
this.timeout(15000);
var data = {
a: 'aa',
b: 'bb'
};
module.save(data, function(err, res) {
should.not.exist(err);
done();
});
});
it('Get Data By Id', function(done) {
var id = "28ca9";
module.get(id, function(err, res) {
console.log(res);
should.not.exist(err);
done();
});
});
});
You can either set the timeout when running your test:
mocha --timeout 15000
Or you can set the timeout for each suite or each test programmatically:
describe('...', function(){
this.timeout(15000);
it('...', function(done){
this.timeout(15000);
setTimeout(done, 15000);
});
});
For more info see the docs.
I find that the "solution" of just increasing the timeouts obscures what's really going on here, which is either
Your code and/or network calls are way too slow (should be sub 100 ms for a good user experience)
The assertions (tests) are failing and something is swallowing the errors before Mocha is able to act on them.
You usually encounter #2 when Mocha doesn't receive assertion errors from a callback. This is caused by some other code swallowing the exception further up the stack. The right way of dealing with this is to fix the code and not swallow the error.
When external code swallows your errors
In case it's a library function that you are unable to modify, you need to catch the assertion error and pass it onto Mocha yourself. You do this by wrapping your assertion callback in a try/catch block and pass any exceptions to the done handler.
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(function (err, result) {
try { // boilerplate to be able to get the assert failures
assert.ok(true);
assert.equal(result, 'bar');
done();
} catch (error) {
done(error);
}
});
});
This boilerplate can of course be extracted into some utility function to make the test a little more pleasing to the eye:
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(handleError(done, function (err, result) {
assert.equal(result, 'bar');
}));
});
// reusable boilerplate to be able to get the assert failures
function handleError(done, fn) {
try {
fn();
done();
} catch (error) {
done(error);
}
}
Speeding up network tests
Other than that I suggest you pick up the advice on starting to use test stubs for network calls to make tests pass without having to rely on a functioning network. Using Mocha, Chai and Sinon the tests might look something like this
describe('api tests normally involving network calls', function() {
beforeEach: function () {
this.xhr = sinon.useFakeXMLHttpRequest();
var requests = this.requests = [];
this.xhr.onCreate = function (xhr) {
requests.push(xhr);
};
},
afterEach: function () {
this.xhr.restore();
}
it("should fetch comments from server", function () {
var callback = sinon.spy();
myLib.getCommentsFor("/some/article", callback);
assertEquals(1, this.requests.length);
this.requests[0].respond(200, { "Content-Type": "application/json" },
'[{ "id": 12, "comment": "Hey there" }]');
expect(callback.calledWith([{ id: 12, comment: "Hey there" }])).to.be.true;
});
});
See Sinon's nise docs for more info.
If you are using arrow functions:
it('should do something', async () => {
// do your testing
}).timeout(15000)
A little late but someone can use this in future...You can increase your test timeout by updating scripts in your package.json with the following:
"scripts": {
"test": "test --timeout 10000" //Adjust to a value you need
}
Run your tests using the command test
For me the problem was actually the describe function,
which when provided an arrow function, causes mocha to miss the
timeout, and behave not consistently. (Using ES6)
since no promise was rejected I was getting this error all the time for different tests that were failing inside the describe block
so this how it looks when not working properly:
describe('test', () => {
assert(...)
})
and this works using the anonymous function
describe('test', function() {
assert(...)
})
Hope it helps someone, my configuration for the above:
(nodejs: 8.4.0, npm: 5.3.0, mocha: 3.3.0)
My issue was not sending the response back, so it was hanging. If you are using express make sure that res.send(data), res.json(data) or whatever the api method you wanna use is executed for the route you are testing.
Make sure to resolve/reject the promises used in the test cases, be it spies or stubs make sure they resolve/reject.