mocha async test is timing out - node.js

Hi I am new to testing with Mocha let alone async testing. I keep getting the following error when running this test. I have spend a lot of time researching the resolution on the web but no luck.
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
it('Should fail to create new user due to missing email', (done) => {
const user_empty_email = {
name: "First Name",
email: "",
password: "password",
isAdmin: false
}
chai.request(app).post('/v1/users')
.send(user_empty_email)
.then((res) => {
expect(res).to.have.status(400);
done();
}).catch(done)
})
Below is an example response I am getting fir the /v1/users
{
"user": {
"_id": "5de4293d3501dc21d2c5293c",
"name": "Test Person",
"email": "testemail#gmail.com",
"password": "$2a$08$8us1C.thHWsvFw3IRX6o.usskMasZVAyrmccTNBjxpNQ8wrhlBt6q",
"isAdmin": false,
"tokens": [
{
"_id": "5de4293d3501dc21d2c5293d",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZGU0MjkzZDM1MDFkYzIxZDJjNTI5M2MiLCJpYXQiOjE1NzUyMzM4NTN9.mi4YyYcHCvdYrl7OuI5eDwJ8xQyKWDcqgKsXRYtn0kw"
}
],
"__v": 1
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZGU0MjkzZDM1MDFkYzIxZDJjNTI5M2MiLCJpYXQiOjE1NzUyMzM4NTN9.mi4YyYcHCvdYrl7OuI5eDwJ8xQyKWDcqgKsXRYtn0kw"
}

why don't you try increasing the timeout in (ms), it's normal for tests to run slow especially when your testing network requests.
package.json
"test": "mocha --timeout 10000"

Is there a chance that your endpoint actually takes more than 2 seconds to run? If so, you might want to increase the timeout when running Mocha: Change default timeout for mocha.
Also, does your endpoint return a response? If not, increasing the timeout will not help. Could you add the code of the /v1/users endpoint to your question to look into that possibility?

Not sure about this. But as far as i recall mixing Promises and callback-style (done-callback) can cause such problems in mocha.
Try using Promises only:
remove all done from test
actually return the Promise (return chai.request...)

the issue was this test:
mongoDB-connect.js
It was ran before the others. mongoDB connection was being closed which caused the other tests to timeout. When I removed the close command all tests passed as expected.
"use strict";
// NPM install mongoose and chai. Make sure mocha is globally
// installed
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const chai = require('chai');
const expect = chai.expect;
// Create a new schema that accepts a 'name' object.
// 'name' is a required field
const testSchema = new Schema({
name: { type: String, required: true }
});
// mongoose connect options
var options = {
useNewUrlParser: true,
useUnifiedTopology: true
}
//Create a new collection called 'Name'
const Name = mongoose.model('Name', testSchema);
describe('Database Tests', function() {
//Before starting the test, create a sandboxed database connection
//Once a connection is established invoke done()
before(function (done) {
// mongoose.connect('mongodb://localhost:27017',options);
mongoose.connect('mongodb://ip/testDatabase',options);
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error'));
db.once('open', function() {
console.log('We are connected to test database!');
done();
});
});
describe('Test Database', function() {
//Save object with 'name' value of 'Mike"
it('New name saved to test database', function(done) {
var testName = Name({
name: 'Mike'
});
testName.save(done);
});
it('Dont save incorrect format to database', function(done) {
//Attempt to save with wrong info. An error should trigger
var wrongSave = Name({
notName: 'Not Mike'
});
wrongSave.save(err => {
if(err) { return done(); }
throw new Error('Should generate error!');
});
});
it('Should retrieve data from test database', function(done) {
//Look up the 'Mike' object previously saved.
Name.find({name: 'Mike'}, (err, name) => {
if(err) {throw err;}
if(name.length === 0) {throw new Error('No data!');}
done();
});
});
});
//After all tests are finished drop database and close connection
after(function(done){
mongoose.connection.db.dropDatabase(function(){
mongoose.connection.close(done);
});
});
});

Related

How can I either wait or prevent jest from running my db connection in Sequelize, so as to stop the error "Cannot log after tests are done..."

Im trying to add unit tests to my node express app. The app uses sequelize with sqlite as its orm/database. I am trying to unit test one of my simplest controllers directly (not even trying to test routes with supertest yet)
I was able to get one basic successful test, the issue I a having is that since my controller imports my sequelize User model, it also imports the instantiation of Sequelize. This results in sequelize trying to connect to the DB while the tests are performed. The test is executed before all the db stuff happens, and hence at the end my result is a lot of "Cannot log after tests are done" because Sequelize is still trying to connect and log things, some from the sequelize module itself and others from my code connecting and sync()ing the db.
If I remove the import and usage of the model in my controller, I no longer have those issues, so clearly importing the model is causing all those calls to initialize sequelize. How can i ask Jest to either wait until all async processes are done, or just prevent sequelize from initializing at all (I dont actually need a db connection, i will test them all with mocks)
So here is what the test looks like
describe('testing user registration', () => {
let mRes: Response<any, Record<string, any>>;
let mNext;
beforeAll(()=>{
mRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn()
}
mNext = jest.fn(err => err)
jest.spyOn(UserModel, 'create').mockResolvedValue(createdUser)
})
afterAll(()=>{
jest.resetAllMocks();
})
test('with invalid email', async () => {
const result = await registerUser(mReqInvalidEmail, mRes, mNext);
expect(mNext).toBeCalledWith(invalidBodyError)
})
})
Here is what the Controller looks like:
const registerUser = async(req:Request,res:Response,next:NextFunction) : Promise<Promise<Response> | void> => {
const {body} = req
const {email, role} = body;
if(!isValidEmail(email) || !isValidRole(role)){
const error = createError(
400,
'Invalid body, please provide a valid email address a role of oneOf[\"user\",\"super\"]',
{
body: {
email: 'a valid email string',
role: 'string, oneOf [user, super]'
}
}
);
return next(error);
}
const password = generatePassword()
const hash = hashPassword(password)
const user = await User.create({email, role, password:hash}).catch((err: Error) : Error => {
return createError(500, 'woopsie', err)
})
if(user instanceof Error){
return next(user)
}
return res.status(200).json({
email,
role,
password
});
}
The model:
import {sqlzDB} from "../../database/db";
const User = sqlzDB.define('User',
{
...myfields, not including to save space
}, {
options
})
And finally, where sequelize gets initialized (declaring sqlzDB). This is all the code that is running that I need to either wait for it to finish, or just prevent it from getting called at all!
const {Sequelize} = require('sequelize');
export const sqlzDB = new Sequelize({
dialect: 'sqlite',
storage: 'database/db.sqlite'
});
sqlzDB.authenticate()
.then(():void => {
console.log('Connection to database established successfully.');
}).catch((err : Error): void => {
console.log('Unable to connect to the database: ', err);
})
sqlzDB.sync().then(()=>{
console.log('Sequelize Synced')
})
My test passes just fine. Note that for the test i wrote i dont actually need the mocks yet, Since im just trying to get the setup to work correctly.
I have tried suggestions I have seen out here like calling await new Promise(setImmediate); after my test, or also closing the connection with sqlzDB.close() which causes some different issues (it closes the connection but still tries to log)
So i dont know how to approach this at this point! Sorry for th elong question and thanks to whoever took their time to read this!
You can try a trick to avoid this issue: Close the connection in afterAll
afterEach(() => { // reset mock should be in afterEach
jest.resetAllMocks();
});
afterAll(async ()=>{
await sqlzDB.close();
await new Promise(res => setTimeout(res, 500)); // avoid jest open handle error
});

How to restore mongo in chai mocha

So I want to restore mongo database before tests begin.
I do this way:
const app = require("../app");
const chai = require("chai");
const mongoose = require("mongoose");
const User = require('../models/users');
const Region = require('../models/regions');
const testUsers = require('../testdata/users.json');
const testRegions = require('../testdata/regions.json');
describe('Restoring database', function () {
before(function(done) {
var promises = [
User.deleteMany().exec()
,Region.deleteMany().exec()
];
console.log('Cleaned database');
done();
});
before(function(done) {
testUsers.users.forEach(element => {
var ObjectId = mongoose.Types.ObjectId;
element._id = new ObjectId(element._id);
var newUser = new User(element);
newUser.save(function (err, result) {
if (err) {
console.log("err:",err);
}
});
});
console.log('Users added');
done();
});
before(function(done) {
testRegions.regions.forEach(element => {
var newRegion = new Region(element);
newRegion.save(function (err, result) {
if (err) {
console.log("err:",err);
}
});
});
console.log('Regions added');
done();
});
testdata/users.json and testdata/regions.json are simple json files including key/pair values.
Does this look good?
When I run
npm test
It does not give any error. And in the console I see this:
Restoring database
Cleaned database
Users added
Regions added
But when I look in database I get different results.
Sometimes everything looks good. All the rows are in the collections.
Sometimes a few rows are missing in one of the collections.
Sometimes one of the collections is empty.
This is a very strange behaviour.
I also tried to add in the variable "promises" each of the "newUser" and "newRegion" instead of executing them directly.
But I still get these strange results.
Whats the deal?
The issue is due to done() being called before the async statements/promises have completed.
Either use async/await or use Promises and only call done() when your async statements/promises have completed.
For example:
No done() call as we using await statements which will wait till each statement completes before continuing:
before(async function() {
let userResult = await User.deleteMany();
let regionResult = wait Region.deleteMany();
console.log('Cleaned database');
});
Or use done() with promises:
before(function(done) {
User.deleteMany()
.then(result => {
console.log('Cleaned database');
done();
});
});
The syntax in your before example is not adding Promises at all, it is simply adding those functions to an array:
var promises = [
User.deleteMany().exec()
,Region.deleteMany().exec()
];
Take a look at the following related answer to help.

Testing Write Functions with Mocha/Chai

We are in the process of writing unit tests for our Node/MongoDB API using Mocha with Chai. My question involves what's recommended when testing write functions. Specifically, how does one handle testing the results of, for instance, an update() function, without actually updating the record in the database?
Does mocha have some way of handling this specifically? Is the recommendation to use a test db? Or to roll back changes after they're made? Or some other option?
Here's an example of one of our test functions:
describe("remove()", function() {
let testTeammember, results;
before(async function() {
// Look for a teammember using standard MongoDB syntax
testTeammember = await db.collection("teammembers").findOne();
if (!mongoose.Types.ObjectId.isValid(testTeammember._id)) fail("Failed because id is not a valid Mongo ObjectId");
// Confirm that a teammember is available for testing
if (!testTeammember) throw "Failed to load test teammember";
const teamId = testTeammember._id.toString();
const params = { id: teamId };
const auth = await TestHelper.generateTestAuth("asmith", apikey);
results = await TeamCtlr.remove(params, auth);
});
it("should be truthy if passed valid params and auth", function() {
assert.isOk(results);
});
it("should return a number for the 'count' property", function() {
assert.typeOf(results.count, "number");
});
it("should have a 'count' of 1", function() {
expect(results.count).to.equal(1);
});
it("results should have a property 'data'", function() {
expect(results).to.have.property("data");
});
it("should return an object for the 'data' property", function() {
assert.isObject(results.data);
});
it("should have a property 'deleted' of type 'boolean'", function() {
assert.isBoolean(results.data.deleted);
});
it("should have 'deleted' set to 'true'", function() {
expect(results.data.deleted).to.equal(true);
});
it("should return a JSON object", function() {
expect(results).to.exist;
});
});
So, for the above code, I want to test that the remove() function works as expected. But I don't want to end up actually saving these changes to that record in the database.

Mongoose Models & Integration Testing Race Conditions

Haven't been able to find any answers to these questions through Twitter or the Mongoose JS Gitter channel and would appreciate some help.
I'm writing an API using a Hapi.JS and Mongoose. I'm using a test database for my integration tests. What I've found though is that if I clear the database after more than one describe block it negatively effects my ability to save and run queries in subsequent describe blocks. I'll leave some annotated code below.
How can I clear the database after each test and not have any race conditions that effect other tests?
'use strict'
//this test will pass when run alone
// it clears the db at the end of it's run
let testConfig = require('../fixtures/fixtures.js')
let User = require('../../models/user.js')
let Bucket = require('../../models/bucket.js')
let BucketFactory = require('../../factories/bucket-factory.js')
let request = require('request')
let bluebird = require('bluebird')
let mongoose = require('mongoose')
mongoose.Promise = bluebird
describe('Buckets API', function() {
it('should get all buckets', function(done){
request.get(`${testConfig.testConfig.testUrl}/buckets`, (err, response, body)=>{
if (err){ throw new Error(err)}
expect(response.statusCode).toBe(200)
done()
})
})
it('should get a bucket by its id', function (done) {
request.get(`${testConfig.testConfig.testUrl}/buckets/${mongoose.Types.ObjectId()}`, (err, response, body)=>{
expect(response.statusCode).toBe(200)
done()
})
});
it('should post it', function (done) {
let testBucket = {name: "Drill", userId: mongoose.Types.ObjectId()}
request.post(`${testConfig.testConfig.testUrl}/buckets`, {json: testBucket}, (err, response, body)=>{
if(err){ throw new Error(err)}
expect(response.statusCode).toEqual(200)
done()
} )
});
// i dont need this afterEach
// but for illustrative purposes it will mess up the latter test
// which will pass if i run it by itself
// but it shouldnt right?
afterEach((done)=>{
Bucket.remove({}).then(()=>{done()})
})
})
describe('findByAndUpdate', function () {
beforeEach((done)=>{
// this factory creates and saves a bucket
// i've verified this by checking the test database manually
let newBucket = BucketFactory
done()
})
it('should find and update', function (done) {
Bucket.find({}).exec()
.then((data)=>{
request.put(`${testConfig.testConfig.testUrl}/buckets/${data._id}`, {json: {name: 'Marvel'}}, (err, response, body)=>{
if(err){ throw new Error(err)}
console.log(body)
expect(response.statusCode).toEqual(200)
done()
} )
})
});
afterEach((done)=>{
Bucket.remove({}).then(()=>{done()})
})
});

Test Node.js API with Jest (and mockgoose)

Two questions here:
1) Is Jest a good options to test Node.js (express) APIs?
2) I'm trying to use Jest with Mockgoose, but I can't figure out how to establish the connection and run tests after. Here is my final attempt before coming on SO:
const Mongoose = require('mongoose').Mongoose
const mongoose = new Mongoose()
mongoose.Promise = require('bluebird')
const mockgoose = require('mockgoose')
const connectDB = (cb) => () => {
return mockgoose(mongoose).then(() => {
return mongoose.connect('mongodb://test/testingDB', err => {
if (err) {
console.log('err is', err)
return process.exit()
}
return cb(() => {
console.log('END') // this is logged
mongoose.connection.close()
})
})
})
}
describe('test api', connectDB((end) => {
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3)
})
end()
}))
The error is Your test suite must contain at least one test. This error makes a bit of sense to me but I can't figure out how to solve it. Any suggestions?
Output:
Test suite failed to run
Your test suite must contain at least one test.
Very late answer, but I hope it'll help.
If you pay attention, your describe block has no test function inside it.
The test function is actually inside the callback passed to describe.. kind of, the stack is complicated due to arrow function callbacks.
this example code will generate the same problem..
describe('tests',function(){
function cb() {
setTimeout(function(){
it('this does not work',function(end){
end();
});
},500);
}
cb();
setTimeout(function(){
it('also does not work',function(end){
end();
});
},500);
});
since the connection to mongo is async, when jest first scans the function to find "tests" inside the describe, it fails as there is none.
it may not look like it, but that's exactly what you're doing.
I think in this case your solution was a bit too clever(to the point it doesn't work), and breaking it down to simpler statements could've helped pinpointing this issue
var mongoose = require('mongoose');
// mongoose.connect('mongodb://localhost/animal', { useNewUrlParser: true });
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
var kittySchema = new mongoose.Schema({
name: String
});
var god = mongoose.model('god', kittySchema);
module.exports = god;
code for god.js file
describe("post api", () => {
//life cycle hooks for unit testing
beforeAll(() => {
mongoose.connect(
"mongodb://localhost/animal",
{ useNewUrlParser: true }
);
});
//test the api functions
test("post the data", async () => {
console.log("inside the test data ");
var silence = await new god({ name: "bigboss" }).save();
});
// disconnecting the mongoose connections
afterAll(() => {
// mongoose.connection.db.dropDatabase(function (err) {
// console.log('db dropped');
// // process.exit(0);
// });
mongoose.disconnect(done);
});
});
Jest testing code ..using jest we can we store the name in db

Resources