Jest not closing because of Mongoose.model - node.js

I am trying to create my first test with jest.
user_model_test.js
const mongoose = require('mongoose')
const User = require('../user_model')
describe('user model tests', () => {
beforeAll( async () => {
await mongoose.connect('mongodb://localhost/supertest21')
})
afterAll( async () => {
await mongoose.connection.close()
})
it("has a module", () => {
expect(User).toBeDefined()
})
})
user_model.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const userSchema = new Schema({
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
email: {
type: String,
required: true
}
})
const User = mongoose.model('User', userSchema, 'user')
module.exports = User
When I run my test, running with --detectOpenHandles I get this error:
Jest has detected the following 1 open handle potentially keeping Jest from exiting:
● PROMISE
17 | })
18 |
> 19 | const User = mongoose.model('User', userSchema, 'user')
| ^
20 |
21 | module.exports = User
at Function.init (node_modules/mongoose/lib/model.js:970:16)
at Mongoose.Object.<anonymous>.Mongoose.model (node_modules/mongoose/lib/index.js:396:11)
at Object.<anonymous> (libs/user/user_model.js:19:23)
at Object.<anonymous> (libs/user/__tests__/user_model_test.js:3:14)
I know that there is something to do with the mongoose.model initialization.When I pass the 4th parameter to mongoose.model, to skip initialization, the promise error don't show up but the test never closes and don't show any more errors. Any ideas?

Using a setup file and setupFilesAfterEnv
Jest can call a "setup.js" file to run some beforeAll and afterAll functions. As ongoing Mongoose's connections keep Jest open, we will close them using the afterAll hook.
Solution
In your jest.config.js, add the following lines:
setupFilesAfterEnv: [
'<rootDir>/tests/setup.js', // <- Feel free to place this file wherever it's convinient
],
Create a setup.js file with the following code:
import { getMongoDBInstance } from '../src/bin/server.ts';
afterAll(async () => {
const mongoDB = getMongoDBInstance();
await mongoDB.connection.close();
});
Here, getMongoDBInstance is a function that returns me the instance of Mongoose I instantiate when my server boots.
let mongoDB;
async function initServer() {
...
await mongoose.connect(uristring, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
mongoDB = mongoose;
...
}
export const getMongoDBInstance = () => mongoDB;
And now after all tests ran, Jest will call this function and close any MongoDB connections! You can follow the same to solve open connections with knex, or any other node ORMs.

Try changing await mongoose.connection.close() to await mongoose.disconnect();. Worked for me.

Related

Mongoose is not connecting to local database

This is my app.js file. Please help me out to connect it with my local database. Sometimes it gets connected to the database and logs to the console but it doesn't add any collection to the local database.
const mongoose = require('mongoose')
main().catch(err=>console.log(err))
async function main() {
await mongoose.connect("mongodb://localhost:27017/fruitsDB", {
useNewUrlParser: true,
useUnifiedTopology: true
});
//Creating new schema
const fruitSchema = new mongoose.Schema({
name: String,
rating: Number,
review: String
});
const Fruit = mongoose.model("Fruit", fruitSchema);
const fruit = new Fruit ({
name: "Apple",
rating: 7,
review: "Pretty solid"
});
await fruit.save()
}
Insist localhost use 127.0.0.1:27017 This will work for sure.
OR
This happened probably because the MongoDB service isn't started. Follow the below steps to start it:
Go to Control Panel and click on Administrative Tools.
Double-click on Services. A new window opens up.
Search MongoDB.exe. Right-click on it and select Start
The server will start. Now execute npm start again and the code might work this time.
You can use mongo connection like this in typescript for ES6.
Schema like below
import mongoose from "mongoose"
export const RequestLogsSchema = new mongoose.Schema(
{
request_id: String,
...
},
{
collection: "request_logs"
}
)
example connection like below
import mongoose from 'mongoose'
import { RequestLogsSchema } from './mongo-schemas/RequestLogsSchema'
export class MongoClient {
mongodb: any
constructor(private host: string) { }
async MongoConnect() {
return new Promise(async (resolve, _reject): Promise<void> => {
console.log('🟡 MongoDB Connecting !')
this.mongodb = await mongoose.connect(this.host).then(() => {
console.log('🟢 MongoDB Connected !')
resolve(true)
}).catch((err) => console.log(err))
})
}
}
export const schemas = {
RequestLogsModal: mongoose.model("RequestLogs", RequestLogsSchema),
...
}
new MongoClient('mongodb://username:password#localhost:27017/db_name?authSource=db_name').MongoConnect()
To save your data like
import { schemas } from '../connections/mongo'
const saver = (data) => {
const request_logs = new schemas.RequestLogsModal({
request_id: data.request_id,
...
})
await request_logs.save()
}

Why am I not able query ( User.findOne() ) my mongoDB after setting up a Schema?

I can't seem to query my MongoDB after setting up the MongoDB schema. I don't understand where I am going wrong with my MongoDB schema, any help is appreciated!
I WAS earlier successful in querying my mongodb before creating a schema, using queries like this:
const uri = process.env.MONGODB_URI;
const client = new MongoClient(uri);
const result = await client.db("inride_adverts").collection("adverts").findOne({OrgEmail: OrgEmailToSignIn});
However, according to a YouTube tutorial am following (10:40 mins), after setting up a mongodb schema, I am NOT successful in using the following query to interact with my mongodb:
User.findOne( {OrgEmail: signUpEmail} ).exec();
Find below my simple User Schema:
./models/User.js
import { mongoose} from 'mongoose';
const UserSchema = new mongoose.Schema({
OrgName: {
type: String,
required: true
},
OrgEmail: {
type: String,
required: true
},
OrgPwd: {
type: String,
required: true
}
}, { collection: 'adverts' });
export const User = mongoose.model('User', UserSchema);
Also, find below my server.js file
./server.js
import express from 'express';
import { User } from './models/User.js';
import mongodb from 'mongodb';
import { mongoose} from 'mongoose';
mongoose.connect(db, { useNewUrlParser: true })
mongoose.connect(db, { useNewUrlParser: true })
.then( () => console.log("MongoDB Connected..." ))
.catch(error => console.log(err))
app.route('/advertiserRegister')
.post( async (req, res) => {
let formData = req.body;
let signUpName = formData.signUpName;
let signUpEmail = formData.signUpEmail;
let signUpPwd = formData.signUpPwd;
console.log("signUpName: " +signUpName);
console.log("signUpEmail: " +signUpEmail);
console.log("signUpPwd: " +signUpPwd);
if(signUpPwd !== signUpPwdConfirm){
console.log("Passwords arent EQUAL!");
return 0;
} else {
try{
console.log("signUpEmail>>> : " + signUpEmail );
// Validation passed!
const testing = await User.findOne( {OrgEmail: signUpEmail} ).exec();
console.log("testing >>> : " ,testing );
res.redirect('/advertiserLogin')
} catch {
//console.error('error', code);
console.log("Error logging in ");
res.redirect('/advertiserRegister')
};
}
});
The server.js file yields:
MongoDB Connected...
signUpName: Merc Enterprise LTD
signUpEmail: hundredcent.a#gmail.com
signUpPwd: 555
signUpEmail>>> : hundredcent.a#gmail.com
testing >>> : **null**
Error logging in
Turns out that the reason I was NOT able to query my collection was due to the fact that I was unknowingly querying the incorrect collection being: adverts under the incorrect database being: inride_adverts.
I came to understand and realise that In my ./models/User.js, the mongoose.model('User', UserSchema); code creates a new database in Atlas MongoDB called test and creates a totally new collection called User.
Having understood this, I am able to populate and query this collection successfully!

Cannot read property 'create' of undefined in expressjs

Please am new to Nodejs but am trying to insert a data into my database using sequelize but am getting Cannot read property .create of undefined.
This is my index.js file
const fs = require('fs')
const path = require('path')
const Sequelize = require('sequelize')
const config = require('../config/config')
const db = {}
const sequelize = new Sequelize(
config.DB,
config.USER,
config.PASSWORD,
{
host: config.HOST,
dialect: config.dialect,
operatorsAliases: false,
pool: {
max: config.pool.max,
min: config.pool.min,
acquire: config.pool.acquire,
idle: config.pool.idle
}
});
fs
.readdirSync(__dirname)
.filter((file) =>
file !== 'index.js'
)
.forEach((file) => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes)
// db[model] = model
db.User = model
})
db.sequelize = sequelize
db.Sequelize = Sequelize
module.exports = db;
This is my Model/User.js File
module.exports = (sequelize, DataTypes) => {
sequelize.define('User', {
hmtec_email: {
type: DataTypes.STRING,
unique: true
},
hmtec_password: DataTypes.STRING
})
This is my Controllers/AuthController.js File
const {User} = require ('../models/User')
module.exports = {
async register (req, res) {
try {
const user = await User.create(req.body)
res.send(user.toJSON())
} catch (err) {
console.log(err);
res.status(400).send({
error: 'Email already in Use'
})
}
}
I don't know what wrong with the code, I dont know why am getting that error of .create is undefined
I think your problem is in the last file .. AuthController.js
const {User} = require ('../models/User')
You are using the Es6 new destruction assignment
More Info here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Normally you should define your variable without the brackets
That mean you got all of the variable available.
But adding the brackets means that you want to get the child of the object, this child is named user .. and then name the variable also as user
and search these info
From the require file after the equal.
But in your user file .. you are exporting a function .. that does not have any child named user
Thus undefined
Better alternative is to use Classes
More info here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
So in user model
// Inside your function you need to use sequalize module , iam not familar with it but you must require it to use it
const Sequelize = require('sequelize');
// Define the Class
class user {
// add the name you want to use for the function - here I used create so the other code also work - also I inserted async because this is asynchronous and also I removed the sequalize from parameters because you don't need it now after it is required above.
async create (DataTypes) => {
await Sequelize.define('User', {
hmtec_email: {
type: DataTypes.STRING,
unique: true
},
hmtec_password: DataTypes.STRING
})
}
module.exports = user;
Now when you require the file in Authcontroller , remove the brackets
const User = require ('../models/User')
and now you can use User.create(YOUR_DATA_VARIABLE);in your authController.js
and if there is any other functions inside this class you also can use them in the same manner.
I hope this fix your problem.

Mongoose can't find recent saved

I am trying to save the IP address of the client who connects to my script.
However, while I am not getting any errors, when I check the collection it is empty.
index.js (main app)
const Listeners = require('mongoose').model('listeners');
const userData = {"ipaddress":ip}
const Listener = new Listeners(userData);
Listener.save(function (err, userData) {
if (err) return console.error(err);
});
Mongoose index.js
const mongoose = require('mongoose');
module.exports.connect = (uri) => {
mongoose.connect(uri, {useCreateIndex: true, useFindAndModify: false , useNewUrlParser: true, useUnifiedTopology: true });
// plug in the promise library:
mongoose.Promise = global.Promise;
mongoose.connection.on('open',function() {
console.log('Mongoose connected. what did you think');
});
mongoose.connection.on('error', (err) => {
console.error(`Mongoose connection error: ${err}`);
process.exit(1);
});
// load models
require('./listener');
};
My listener file
const mongoose = require('mongoose');
// define the User model schema
const ListenerSchema = new mongoose.Schema({
ipaddress: {
type: String,
// index: { unique: true }
},
station: String,
start_time:{
type: Date
},
end_time:{
type: Date
}
}, { collection: 'listeners'});
/**
* The pre-save hook method.
*/
ListenerSchema.pre('save', function saveHook(next) {
const Listener = this;
console.log(this)
});
module.exports = mongoose.model('listeners', ListenerSchema);
when I run it I get { _id: 5e2bf98549ae2d5d6da52475, ipaddress: '127.0.0.1' }
However when I open the mongodb collection I see nothing.
There seems to not be an error, but there must be?
💡 The only one reason why you can't save your data into your collection it's because this code in your listener file.
👨‍🏫 Try to comment this code below 👇:
/**
* The pre-save hook method.
*/
ListenerSchema.pre('save', function saveHook(next) {
const Listener = this;
console.log(this)
});
👨‍🏫 or, the second option is add next function in there. So, your code will looks like this code below 👇:
/**
* The pre-save hook method.
*/
ListenerSchema.pre('save', function saveHook(next) {
const Listener = this;
console.log(this);
// add next function below
next();
});
And now, you can try again and I'm sure, you can see the collection in your mongodb.
I hope it's can help you 🙏.

Mocking Mongoose model with jest

I am trying to mock a mongoose model with jest, but is getting Cannot create property 'constructor' on number '1' error. I was able to reproduce the issue by creating the project with 2 files shown below. Is there a way to mock a mongoose model with jest?
./model.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const schema = new Schema({
name: String
})
module.exports = mongoose.model('Test', schema)
./model.test.js
jest.mock('./model')
const Test = require('./model')
// Test.findOne.mockImplementation = () => {
// ...
// }
Error:
FAIL ./model.test.js
● Test suite failed to run
TypeError: Cannot create property 'constructor' on number '1'
at ModuleMockerClass._generateMock (../../jitta/sandbox/rest_api/node_modules/jest-mock/build/index.js:458:34)
at Array.forEach (native)
at Array.forEach (native)
at Array.forEach (native)
Update:
Seems to be a bug in jest.
https://github.com/facebook/jest/issues/3073
An other solution is to spyOn the model prototype functions.
For example, this will make MyModel.save() fail :
jest.spyOn(MyModel.prototype, 'save')
.mockImplementationOnce(() => Promise.reject('fail update'))
You can use mockImplementationOnce to not having to mockRestore the spy. But you can also use mockImplementation and use something like :
afterEach(() => {
jest.restoreAllMocks()
})
Tested with "mongoose": "^4.11.7" and "jest": "^23.6.0".
ok, i had the same problem so i author this package to solve this problem:
https://www.npmjs.com/package/mockingoose
this is how you can use it let's say this is your model:
import mongoose from 'mongoose';
const { Schema } = mongoose;
const schema = Schema({
name: String,
email: String,
created: { type: Date, default: Date.now }
})
export default mongoose.model('User', schema);
and this is your test:
it('should find', () => {
mockingoose.User.toReturn({ name: 2 });
return User
.find()
.where('name')
.in([1])
.then(result => {
expect(result).toEqual({ name: 2 });
})
});
checkout the tests folder for more examples:
https://github.com/alonronin/mockingoose/blob/master/___tests___/index.test.js
no connections is made to the database!
for typescript I found a hack that works
jest.spyOn(model, 'find').mockReturnValueOnce(dummyData as any);
Mockingoose seems to be a very nice solution. But I was also able to mock my model with Jest.mock() function. At least create method.
// in the module under the test I am creating (saving) DeviceLocation to DB
// someBackendModule.js
...
DeviceLocation.create(location, (err) => {
...
});
...
DeviceLocation model definition:
// DeviceLocation.js
import mongoose from 'mongoose';
const { Schema } = mongoose;
const modelName = 'deviceLocation';
const DeviceLocation = new Schema({
...
});
export default mongoose.model(modelName, DeviceLocation, modelName);
DeviceLocation mock in the __mocks__ folder in the same folder as DeviceLocation model:
// __mock__/DeviceLocation.js
export default {
create: (x) => {
return x;
},
};
in the test file:
// test.js
// calling the mock
...
jest.mock('../../src/models/mongoose/DeviceLocation');
import someBackendModule from 'someBackendModule';
...
// perform the test

Resources