Mocha test fail ensure don() is called - javascript

Hi I have strange problem while testing code with Mocha:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves
Here is code:
describe('POST /notes', () => {
it('should create new note', (done) => {
const title = 'Test title';
const text = 'Test text';
const category = 'Test category';
request(app)
.post('/notes')
.send({title, text, category})
.expect(200)
.expect(res => {
expect(res.body.title).toBe(title);
})
.end((err, res) => {
if (err)
return done(err);
Note.find({text: text}).then(notes => {
expect(notes.length).toBe(1);
expect(notes[0].title).toBe(title);
done();
}).catch(err => done(err));
});
});
it('should not create new note with invalid body data', done => {
request(app)
.post('/notes')
.send({})
.expect(400)
.end((err, res) => {
if (err)
return done(err);
Note.find().then(notes => {
expect(notes.length).toBe(notesDummy.length);
done();
}).catch(err => done(err));
});
})
});
First test fails with error described above. As it comes to the second, it passes. Both tests are similar and I don't know what I am missing... Any ideas ?

If you are interacting with a live database, it's possible that the operation just takes longer than 2 seconds to complete. The test for a successful operation would take longer than the negative test if you have server-side validation happening before interacting with the database.
You can extend the timeout in mocha using this.timeout(<some number in milliseconds>) :
it('should create new note', (done) => {
this.timeout(9000); // set it to something big to see if it fixes your issue
const title = 'Test title';
const text = 'Test text';
const category = 'Test category';
request(app)
.post('/notes')
.send({title, text, category})
.expect(200)
.expect(res => {
expect(res.body.title).toBe(title);
})
.end((err, res) => {
if (err)
return done(err);
Note.find({text: text}).then(notes => {
expect(notes.length).toBe(1);
expect(notes[0].title).toBe(title);
done();
}).catch(err => done(err));
});
});
The only other thing I can think of is that your server side code is hanging somewhere and not sending a response (or that Notes.find() is not resolving or rejecting for some reason). Your test code looks fine to me.

I'm not 100% sure that the expects outside of promise-land at the beginning will cause done to be called if they fail:
.expect(200)
.expect(res => {
expect(res.body.title).toBe(title);
})
Maybe add some logging to see whether it ever gets to your .end handler?
Also, what are you importing to get .expect methods on your request? We should look at its docs to see about done hooks.

Related

Testing mongoose queries in jest - just the first test block performs the query

//first test suite
test('queries first time', async (done) => {
mockingoose(Model).toReturn({ name: 'asd' });
const doc = await Model.find({})
console.log(doc); //right value
mockingoose.resetAll();
done();
})
test('queries second time', async (done) => {
mockingoose(Model).toReturn({ name: 'asd' });
const doc = await Model.find({})
console.log(doc); //undefined
mockingoose.resetAll();
done();
})
//second test suite without mocking the data
test('queries first time', (done) => {
Model.find({}, (err, doc) => {
console.log(err, doc); //logs values
return
});
done();
})
test('queries second time', (done) => {
Model.find({}, (err, doc) => {
console.log(err, doc); //doesn't log
return
});
done();
})
So the issue is that while testing mongoose with jest, just the first test block's query runs. The mocking is to actually have some result of querying on the model, but with or without it, the second block of test never works. It doesn't even throw an error, it just always returns undefined, or as in the second test suite, the callback function doesn't get called.
Any ideas?

How to make mocha wait before moving to the next test

When i ran npm test i got a 'TypeError [ERR_HTTP_INVALID_HEADER_VALUE]: Invalid value "undefined" for header "x-access-token"' error. Seems like mocha moves on to the second test before getting the token. I tried adding a delay with the setTimeOut method but i still got the above error.
// creates valid-user object
const validUser = {
username: 'Rigatoni',
email: 'yahoo.com',
password: 'qwerty1234567',
};
describe('Post Tests', () => {
// login and get token...
let token;
before((done) => {
request(app)
.post('/api/v1/auth/login')
.send(validUser)
.end((err, res) => {
// eslint-disable-next-line prefer-destructuring
token = res.body.token;
console.log('token', token);
expect(res.status).to.equal(200);
});
// console.log('token test');
done();
});
describe('GET all posts', () => {
it('should return all posts', (done) => {
request(app)
.get('/api/v1/posts')
.set('x-access-token', token)
.end((err, res) => {
expect(res.body.success).to.equal(true);
});
done();
});
});
});
Your tests are almost right!
The done callback is provided to let Mocha know when it's OK to move on. However, you are calling done() in your tests right after calling the asynchronous request method; Mocha thinks the test is done before you even make the request.
Move your done() call for each test into a callback function (for example, on the line right after your expect()), so that it isn't executed until after the request completes. Then Mocha will wait until the test is over before moving on.
Example:
request(app)
.get('/api/v1/posts')
.set('x-access-token', token)
.end((err, res) => {
expect(res.body.success).to.equal(true);
done();
});

Mocha before hook is not working with chai-http

This is my test code. I'm testing an API. The problem is the "after" hook is working and dropping database after the test ends. But the "before" hook is not working. what's the issue here? I tried but unable to find out the issue. I tried to run the before hook with only one dummy test like logging something in the console. Didn't work either.
const chai = require('chai');
const { assert } = require('chai');
const chaiHttp = require('chai-http');
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
require('../resolvedir');
const User = require('models/User');
const server = require('bin/www');
const testData = require('./test_data');
chai.use(chaiHttp);
describe('Empty User Collection before test', function () {
it('should drop User Collection before test starts', function () {
before(function (done) {
User.collection.drop();
done();
});
});
});
describe('Testing /registration end point', () => {
it('should return a valid JWT token', (done) => {
chai.request(server)
.post('/register')
.send({ name: testData.name, email: testData.email, password: testData.password })
.end((err, res) => {
assert.equal(res.status, 200, 'Http response code is 200');
assert.exists(res.body.auth, 'Auth confirmation message exist');
assert.isTrue(res.body.auth, 'Auth confirmation message is true');
assert.exists(res.body.token, 'JWT token is neither null or undefined');
assert.isString(res.body.token, 'JWT token is string');
done();
});
});
it('should fail registration', (done) => {
chai.request(server)
.post('/register')
.send(testData)
.end((err, res) => {
assert.equal(res.status, 409, 'Http response code is 409');
assert.isString(res.body.message);
assert.equal(res.body.message, 'User Exist');
done();
});
});
});
describe('Testing /login end point', function () {
it('should get a valid JWT token on successful login', function (done) {
chai.request(server)
.post('/login')
.send({ email: testData.email, password: testData.password })
.end((err, res) => {
assert.isString(res.body.token, 'JWT token is string');
done();
});
});
});
describe('Empty User Collection after test', function () {
it('should drop User Collection after test ends', function () {
after(function (done) {
User.collection.drop();
done();
});
});
});
Mongoose and MongoDB support promises. chai-http supports promises as well.
before should reside inside describe block, not inside it.
Mocha supports promises for asynchronous blocks, no done is necessary. But the tests use done and in inconsistent way. before is asynchronous, but done() is called synchronously. done() is called only on success in tests, when assertion fails this results in test timeout.
It should be:
describe('Empty User Collection before test', function () {
before(function () {
return User.collection.drop();
});
it('should drop User Collection before test starts', function () {
...
});
});
And
it('should get a valid JWT token on successful login', function () {
return chai.request(server)
.post('/login')
.send({ email: testData.email, password: testData.password })
.then((res) => {
assert.isString(res.body.token, 'JWT token is string');
});
});
And etc.
If a database should be dropped in Testing /registration end point test suite, there should before that drops a database as well - or all tests can have parent describe with before:
describe('Suite', function () {
before(function () {
return User.collection.drop();
});
describe('Testing /registration end point', () => {...})
describe('Testing /login end point', () => {...})
...
});

Mocha throwing errors when testing node.crypto functions

I am trying to test my password hashing function but I keep on getting failures due to some TypeError error.
I am sure the function works since I tried to call it from some other file and I get the expected result without any error.
Here is the function:
exports.hashPassword = (password) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(salt_length, (err, buf) => {
if (err) reject(err);
const salt = buf.toString('base64');
crypto.pbkdf2(password, salt, iterations, keylen, digest, (err, key) => {
if (err) reject(err);
const hashedPassword = '{X-PBKDF2}:'+digest+':'+keylenB64+':'+iterationsB64+':'+salt+':'+key.toString('base64');
resolve(hashedPassword);
});
});
});
};
Here is the test that fails:
describe('users', () => {
describe('utils', () => {
it('should hash a password', (done) => {
const password = 'secret';
utils.hashPassword('secret')
.then((hash) => {
console.log('Hash: '+ hash);
done();
})
.catch((err) => {
console.log(err);
done(err);
});
});
});
});
And this is the 'error':
1 failing
1) users utils should hash a password:
TypeError: size must be a number >= 0
at Promise (api\paths\users\utils.js:24:12)
at Object.exports.hashPassword (api\paths\users\utils.js:14:10)
at Context.it (api\paths\users\utils.test.js:30:13)
Has anyone any idea why?
I am using mocha with should and developing on node.
Thanks to pietrovismara that pointed me towards the solution.
The problem was that salt_length was a string and not a number. I was printing out the arguments and I could see they were 'correct' but obviously not the right Type (hence TypeError, I guess...)
I keep the argument in a .env file and I read them in using the dotenv package which obviously read them as simple strings (as it should)... the reason why it was working when I was 'testing' the function from another file was that in that instance I was not using the arguments read from .env, I have something like:
const salt_length = process.env.SALT_LENGTH || 128;
Mocha was correclty using .env values (strings), but when fooling around with the file I was not loading those enviroment variables.
I learned something today, that is I should go home when tired instead of charging on and not seeing things in front of my eyes.
Also, since mocha supports promises, the 'correct' test case should be (using should):
describe('users', () => {
describe('utils', () => {
it('should hash a password', () => {
return utils.hashPassword('secret').should.be.fulfilled();
});
});
});

Mocha: Multiple "it" calls in single server request

I'm trying to test 40+ API endpoints using Mocha. I would like to perform a few subtests as a part of a single server call.
For example, I would like to test if it('returns valid JSON... and it('returns a valid status code..., etc.
configs.forEach(function(config) {
describe(config.endpoint, () => {
it('...', function(done) {
server
.post(config.endpoint)
.send({})
.expect('Content-type', /json/)
.expect(200)
.end(function(err, res) {
//it('has a proper status code', () => {
expect(res.status).toEqual(200);
//})
//it('does not have an error object', () => {
expect(res.body.hasOwnProperty('error')).toEqual(false);
//})
done();
})
})
})
})
The problem is that I cannot nest it statements, but I am relying on the callback, via done() to dictate when the response has been received, so I have to wrap the call in an it statement...
Because some of these requests take half of a second to resolve, and there are 40+ of them, I don't want to create separate tests for these. Creating separate tests would also duplicate the config.endpoint, and I'd like to see if the tests are passing for each endpoint all in one place.
How can I create multiple tests for a single server call?
Here's how I accomplished this, using mocha, chai, and supertest (API requests):
import { expect } from "chai"
const supertest = require("supertest");
const BASE_URL = process.env.API_BASE_URL || "https://my.api.com/";
let api = supertest(BASE_URL);
describe("Here is a set of tests that wait for an API response before running.", function() {
//Define error & response in the 'describe' scope.
let error, response;
//Async stuff happens in the before statement.
before(function(done) {
api.get("/dishes").end(function(err, resp) {
error = err, response = resp;
done();
});
});
it("should return a success message", function() {
console.log("I have access to the response & error objects here!", response, error);
expect(response.statusCode).to.equal(200);
});
it("should return an array of foos", function() {
expect(response.body.data.foo).to.be.an("array");
});
});
configs.forEach(function(config) {
describe(config.endpoint, () => {
var response;
it('...', function(done) {
server
.post(config.endpoint)
.send({})
.expect('Content-type', /json/)
.expect(200)
.end(function(err, res) {
response=res;
done();
})
});
it('has a proper status code', () => {
expect(response.status).toEqual(200);
})
it('does not have an error object', () => {
expect(response.body.hasOwnProperty('error')).toEqual(false);
})
})
})
What about this ?
I am not sure about nesting of test cases but it will work for u.

Categories

Resources