What's wrong with my mocha test? - node.js

I have written this code in express.js and mongoose
routes.js
var mongoose = require('mongoose'),
logger = require('../logger');
mongoose.connection.on('error', function(){
logger.info('Mongoose connection error' + error);
});
exports.getitems = function(req, res) {
mongoose.connect('mongodb://localhost/abhitest', {auto_reconnect: true, native_parser: true}, function(err){
if (err) logger.error("Error " + err);
});
var Schema = mongoose.Schema;
var User = new Schema({
username : {type: String, required: true},
email : {type: String, required: true}
}, {collection: 'User'});
var UserModel = mongoose.model('User', User, 'User');
UserModel.find({}, function(err, users){
if (!err) {
logger.info('found ' + users);
res.json(200, users);
} else {
logger.error(err);
res.json(404, 'did not find anything');
}
});
};
app.js
var mongoose = require('mongoose'),
express = require('express'),
app = express(),
routes = require('./routes'),
http = require('http');
app.get('/', routes.getitems);
http.createServer(app).listen(3000);
module.exports = app;
When I execute this from the browser. I can see that it returns results correctly from my mongo db.
But when I write this mocha test case for the same code
var app = require('../app'),
request = require('supertest'),
assert = require('assert'),
_ = require('underscore');
describe('when the test runs', function() {
it ('should insert 3 records in mongo db', function(done){
request(app)
.get('/')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res){
if (err) console.log(err);
else {
console.log(res.text);
var items = JSON.parse(res.text);
for(var i = 0; i < items.length; i++) {
var item = items[i];
assert(_.has(item, 'username'));
assert(_.has(item, 'email'));
console.log(item.username);
console.log(item.email);
}
}
});
done();
});
});
The test case output is just
․
1 passing (18ms)
So I am very sure that it doesn't even go inside the end method and it doesn't print anything inside the end method.
I can see that the call goes to the server but it never establishes the connection with mongo. it just hangs as readyState of 2.
So somehow when the test is executing via mocha... it never connects to mongo and is hung on connecting. but when the code is executed via npm start... everything works fine.
What is going wrong with mocha test case?

Your done() call needs to be made from within the request callback:
describe('when the test runs', function() {
it ('should insert 3 records in mongo db', function(done){
request(app)
.get('/')
.expect('Content-Type', /json/)
.expect(200)
.end(function(err, res){
if (err) console.log(err);
else {
console.log(res.text);
var items = JSON.parse(res.text);
for(var i = 0; i < items.length; i++) {
var item = items[i];
assert(_.has(item, 'username'));
assert(_.has(item, 'email'));
console.log(item.username);
console.log(item.email);
}
}
return done();
});
});
});
As it is currently, you call done() before your request is finished.

Related

how to insert data in multiple mongodb collections using mongoose & node.js

i am new in node.js. I'm trying to store three different objects in collection in mongodb using node.js.
index.js
var mongoose = require('mongoose');
var bodyparser = require('body-parser');
var app = express();
var control = require('./controllers/controller');
var port = 3200;
mongoose.connect(
'mongodb://localhost:27017/create-company',
{useNewUrlParser: true},
err => {
if(err) throw err;
console.log('connection successfully');
}
)
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({extended: true}));
app.use('/api', control);
app.listen(port, function(){
console.log('start your server on: ', port);
});
model.js
var mongoose = require('mongoose');
var CompanySchema = new mongoose.Schema({
companyname: String,
founder: String,
contact: String,
location: String
});
var company = mongoose.model("company", CompanySchema);
var BranchSchema = new mongoose.Schema({
branchname: String,
branchmanger: String,
contact: String,
location: String
});
var branch = mongoose.model('branch', BranchSchema);
var UserSchema = new mongoose.Schema({[enter image description here][1]
username: String,
userrole: String,
age: Number
});
var user = mongoose.model('user', UserSchema);
module.exports = {
company: company,
branch: branch,
user: user
}
controller.js
var express = require('express');
var router = express.Router();
var company = require('../models/model');
router.post('/create_company', function (req, res) {
var new_company = new company.company(req.body);
var new_branch = new company.branch(req.body);
var new_user = new company.user(req.body);
new_company.save(function (err, data) {
if (err)
res.send(data);
console.log(data);
});
new_branch.save(function (err, data) {
if (err)
res.send(data);
console.log(data);
});
new_user.save(function (err, data) {
if (err)
res.send(data);
console.log(data);
});
});
i am pass data to postman like this:
[{
"companyname": "stack",
"founder": "alex",
"contact": "1234567890",
"location": "in"
},
{
"branchname": "am",
"branchmanager": "abc",
"contact": "8754216523",
"location": "inn"
},
{
"username": "xyz",
"userrole": "admin",
"age": "23"
}]
enter image description here
There are three problems here:
You are attempting to create a new document by passing the entire body to the constructor. You need to pass the correct array element to it's respective constructor.
var new_company = new company.company(req.body[0]);
var new_branch = new company.branch(req.body[1]);
var new_user = new company.user(req.body[2]);
You are attempting to send a response more than once. You need to coordinate the callbacks so that you send the response after they all have completed. I suggest you use promises to accomplish this, fortunately Mongoose supports promises.
Promise.all([
new_company.save(),
new_branch.save(),
new_user.save()
]).then(function (data) {
res.send(data);
console.log(data);
});
You are not handling the error correctly. Your if (err) statement will cause res.send(data) to be called when there is an error, this is certainly not desired. Using the promise based method defined above error handling is very simple.
Promise.all([
new_company.save(),
new_branch.save(),
new_user.save()
]).then(function (data) {
res.send(data);
console.log(data);
}).catch(function (err) {
res.status(400).send('Uh oh! Something bad happened.');
console.error(err);
});
If you use ES6 syntax you can write a synchronous looking route, which may be more familiar if you are coming from a different language:
router.post('/create_company', async (req, res) => {
try {
const [company, branch, user] = req.body;
const data = await Promise.all([
new company.company(company).save(),
new company.branch(branch).save(),
new company.user(user).save()
]);
res.send(data);
} catch (err) {
res.status(400).send('Uh oh! Something bad happened.');
console.error(err);
}
});

Mongoose can't find my data from database but database has been connected

It has show "Database connected" , I think database has already connected to mongodb server already. However ,there has nothing to be console log. Data is null here and nothing is print on my page.
Here is my code.
index.js
var userModel = require('../models/userModel.js');
router.get('/', function (req, res, next) {
mongoose.connect('mongodb://localhost:27017/db', { useNewUrlParser: true }, function (err) {
if (err) console.log("connect fail");
console.log("Database connected");
});
})
userModel.find({}, function (err, data) {
if (err) {
return console.log(err)
}
console.log(data);
res.render('index', {
title: 'Account', user: data, loginStatus: isLogin
});
})
models/userModel.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
var userSchema = new mongoose.Schema({
name: String,
account: String,
password: String,
email: String
});
var model = mongoose.model('user', userSchema);
module.exports = model;
The problem in this is you're connecting database on api call which should never be the case. You should always do it in the beginning and call the collections at the endpoints. Now whenever you'll call '/' check server console.
var userModel = require('../models/userModel.js');
mongoose.connect('mongodb://localhost:27017/db', { useNewUrlParser: true }, function (err) {
if (err) console.log("connect fail");
console.log("Database connected");
});
router.get('/', function (req, res, next) {
userModel.find({}, function (err, data) {
if (err) {
return console.log(err)
}
console.log(data);
res.render('index', {
title: 'Account', user: data, loginStatus: isLogin
});
})
})

How to display the data stored in MongoDB using Nodejs?

This is the code I have used to display the data from MongoDB. Getting error as "result is not defined". I have used the ID from MongoDB to display the result. But the stored data is not displayed on a separate page.
const express = require('express');
const app = express();
const ejs = require('ejs');
const assert = require('assert');
const bodyParser = require('body-parser');
const MongoClient = require('mongodb').MongoClient;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.set('view engine','ejs');
app.set('')
var url = 'mongodb://localhost:27017';
app.get('/form',(req, res)=>{
res.render('form');
});
app.get('/data',(req, res)=>{
MongoClient.connect(url, (err, db)=>{
assert.equal(null, err);
var dbo = db.db("form");
var details = dbo.collection('usertable').find();
details.forEach((data)=>{
if(_id == 5b5ef0b8e0d9c23c2012cef2){
result = item;
return true;
}
})
});
res.render('customerdata',{item: result });
});
I was able to store the data with the below code:
app.post('/form/submit', (req, res)=>{
var item = {
title: req.body.title,
content: req.body.content,
author: req.body.author
};
MongoClient.connect(url, (err, db)=>{
assert.equal(null, err);
var dbo = db.db("form");
dbo.collection('usertable').insertOne(item, (err, result)=>{
assert.equal(null, err);
console.log('Inserted');
db.close();
});
});
res.redirect('/form');
});
app.listen(8080);
I believe you are facing this problem because
you are not referencing the objects inside the details array properly.
you are also not declaring the result object before entering the forEach loop. And this will result in the value of result being undefined.
Inside your forEach loop, each 'data' is an object. And hence you would have to refer to the id as data._id when you are checking it. Similarly data.item when you are trying to assign the value to result.
I have modified the code. This should work.
app.get('/data',(req, res)=>{
MongoClient.connect(url, (err, db)=>{
assert.equal(null, err);
var dbo = db.db("form");
var details = dbo.collection('usertable').find();
var result;
details.forEach((data)=>{
if(data._id == 5b5ef0b8e0d9c23c2012cef2){
result = data.item;
return true;
}
})
});
res.render('customerdata',{item: result });
});
Several things to note:
MongoClient.connect() is asynchronous, but you are calling res.render() synchronously.
Usually you do not want to initiate a connection to MongoDB at each request.
You should probably do dbo.collection('usertable').find().toArray(function(err, items) {...}); to access the data.
find is async, you have to return the result in the find callback, otherwise it's not defined :
app.get('/data',(req, res)=>{
MongoClient.connect(url, (err, db)=>{
assert.equal(null, err);
var dbo = db.db("form");
dbo.collection('usertable').find({}, function(err, details){
if(err)
{
res.render('customerdata',{item: null });
}
else
{
var result = null;
details.forEach((data)=>{
if(data._id.toString() == "5b5ef0b8e0d9c23c2012cef2"){
result = item;
}
})
res.render('customerdata',{item: result });
}
});
});
In addition your post request has the same mistake, your redirecting the user before the end of the insert operation. Correct code is :
app.post('/form/submit', (req, res)=>{
var item = {
title: req.body.title,
content: req.body.content,
author: req.body.author
};
MongoClient.connect(url, (err, db)=>{
assert.equal(null, err);
var dbo = db.db("form");
dbo.collection('usertable').insertOne(item, (err, result)=>{
assert.equal(null, err);
console.log('Inserted');
db.close();
res.redirect('/form');
});
});
});

Mocha route testing not executing asynchronously

I have started working with mocha and I have a problem with one particular test case. Here is the code:
var assert = require("chai").assert;
var request = require('supertest');
var http = require("http");
var conf = require("../config/config");
var app = require("../app");
var mongoose = require('mongoose');
var User = mongoose.model('User');
describe('User controller', function(){
describe('POST /register', function(){
it('should return false when the parameters are not unique', function (done) {
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:true, redirect:'/user/registerConfirmation'})
.end(function(err, res) {
if (err) {
return done(err);
}
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:false}, done);
});
});
});
I am expecting the result to be false because after the insertion of a user inside the database, the unique index rule should raise an error. When I run this test, I get this: {success: true, redirect: '/user/registerConfirmation'} , I should be getting this: {success: false}. I noticed that when I don't clear the database before each tests (in utils.js) I am getting the expected value. Do I get this error because of an asynchronous error? How can I rewrite this test to make sure that it works?
Thanks
Files
util.js contains the configurations for the test sequence:
'use strict';
process.env.NODE_ENV = 'test';
var config = require('../config/config');
var mongoose = require('mongoose');
beforeEach(function (done) {
mongoose.connection.db.dropDatabase();
return done();
});
afterEach(function (done) {
return done();
});
user.js the user model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
var UserSchema = new Schema({
username: {type: String, required: true, unique: true},
email: {type: String, required: true, unique: true},
password: {type: String, required: true},
status: {type: Number, default:0}
});
UserSchema.virtual('date')
.get(function(){
return this._id.getTimestamp();
});
UserSchema.pre('save', function(next) {
//Password encryption ...
});
mongoose.model('User', UserSchema);
user.js (controller) is the controller for all the user routes.
...
router.post('/register', function (req,res,next){
var newUser = new User({
username: req.body.username
, email: req.body.email
, password: req.body.password
});
newUser.save(function(err){
if(err){
res.send({success: false});
}else{
var newToken = new UserToken({
userId: newUser._id
, email: newUser.email
});
newToken.save(function(err){
if(err){
res.send({success: false});
}else{
res.send({success: true, redirect: '/user/registerConfirmation'});
}
});
}
});
});
...
Edit
I have tried the end() function and it still doesn't work.
There is a problem in how you chain supertest requests in your test test suite - the second request is not invoked properly. When you were not clearing the database the test was failing on the first .expect({success: true, ...}) and you were getting the expected value.
The correct way is to perform the first request with .end method, check for potential errors, and then perform the second request to see if it has failed:
describe('User controller', function(){
describe('POST /register', function(){
it('should return false when the parameters are not unique', function (done) {
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:true, redirect:'/user/registerConfirmation'})
.end(function(err, res) {
// Check if first request has failed (it should not!)
if (err) {
return done(err);
}
// Testing the second, not unique request. that should fail
request(app)
.post('/user/register')
.send({username:"janette_doe", email:"janette_doe#gmail.com", password:"test123"})
.expect('Content-Type',/json/)
.expect({success:false}, done);
});
});
});
In mocha, the done() function should be called when your async call is done - regardless of whether an error is hit. Also, use the .done() function - which is part of the promise API - to end the promise chain and ensure any exceptions are caught. Without the done function, you can miss runtime errors completely, as they are suppressed by the promise library.

Moongose is not defined

I got a 500 error and de description says: Mongoose is not defined when i call /delPost, but in the same file "post.js" the functions /addPost and /getPosts are working and use mongoose.model without problems. im a newbie and have searched a lot but can't fix that issue, hope you can help me.
routes/post.js
var express = require('express');
var mongoose = require('mongoose');
var router = express.Router();
POST message to board (WORKING)
router.post('/addPost', function(req, res) {
if(req.body){
var post = mongoose.model('post');
var newpost = new post(req.body);
console.log(newpost);
newpost.save(function(err, newpost){
if(err) return res.send(200,{msg: 'not saved'});
})
console.log('save ok');
}
else{
res.send(200,{msg: 'no data received'});
}
});
GET messages from DB to show in HTML (WORKING)
router.get('/getPosts', function(req, res) {
var post = mongoose.model('post');
post.find(function(err, posts){
if(err) return console.log(err);
// console.log(posts);
res.send(posts);
})
});
DELETE a post (NOT WORKING)
router.post('/delPost/:rel', function(req, res){
// var mongoose = require('mongoose');
var post = moongose.model('post');
post.remove({_id: req.params.rel},function(err) {
if(err) return handleError(err);
console.log(err);
})
});
AJAX Call to /delPost route
function delPost(event){
event.preventDefault();
console.log('delpost');
var confirmation = confirm('Are you sure to delete this message?');
if(confirmation === true){
$.ajax({
type:'POST',
url: '/post/delPost/' + $(this).attr('rel')
}).done(function(response){
console.log('delete done.. ');
}).fail(function(response){
console.log('delete failed');
})
}
};

Resources