I am writing nodeunit tests for operations around a mongodb. When I execute my test with nodeunit (nodeunit testname.js), the test runs through and goes green but the nodeunit command line doesn't return (I need to hit ctrl-c).
What am I doing wrong? Do I need to close my db connection or the server or is my test wrong?
Here is a cutdown sample test.
process.env.NODE_ENV = 'test';
var testCase = require('/usr/local/share/npm/lib/node_modules/nodeunit').testCase;
exports.groupOne = testCase({
tearDown: function groupOneTearDown(cb) {
var mongo = require('mongodb'), DBServer = mongo.Server, Db = mongo.Db;
var dbServer = new DBServer('localhost', 27017, {auto_reconnect: true});
var db = new Db('myDB', dbServer, {safe:false});
db.collection('myCollection', function(err, collectionitems) {
collectionitems.remove({Id:'test'}); //cleanup any test objects
});
cb();
},
aTest: function(Assert){
Assert.strictEqual(true,true,'all is well');
Assert.done();
}
});
Michael
Try putting your cb() within the remove() callback after you close you connection:
var db = new Db('myDB', dbServer, {safe:false});
db.collection('myCollection', function(err, collectionitems) {
collectionitems.remove({Id:'test'}, function(err, num) {
db.close();
cb();
});
});
You need to invoke cb function after the closure of db (during the tearDown):
tearDown: function(cb) {
// ...
// connection code
// ...
db.collection('myCollection', function(err, collectionitems) {
// upon cleanup of all test objects
db.close(cb);
});
}
This works for me.
Related
I am working on a nodejs / mongodb app using 'mongodb' module. The app is launched with
node main.js
In main.js, I connect to the db and keep the connection in the 'db' global variable. 'db' is then used in inner methods of 'server'. I want to avoid having 'db' as a global variable but did not found the correct way to do.
My current main.js:
var server = require('./lib/server');
var MongoClient = require('mongodb').MongoClient;
var Server = require('mongodb').Server;
var mongoClient = new MongoClient(new Server(HOST, PORT));
db = null;
// Database connection
mongoClient.open(function(err, mongoClient) {
if(!err){
// Database selection
db = mongoClient.db(DB);
// Launch web server
server.start(); // usage of 'db' in this part
} else {
console.log(err.message);
process.exit(1);
}
});
Any idea of a cleaner way ?
UPDATE
I finally created a module in connection.js:
var config = require('../config/config');
var url = 'mongodb://' + config.db.host + ':' + config.db.port + '/' + config.db.name;
var MongoClient = require('mongodb').MongoClient;
var db = null;
module.exports = function(cb){
if(db){
cb(db);
return;
}
MongoClient.connect(url, function(err, conn) {
if(err){
console.log(err.message);
throw new Error(err);
} else {
db = conn;
cb(db);
}
});
}
Each time I need to get the connection I call:
var connection = require('./connection');
connection(function(db){
// doing some stuff with the db
});
This is working very well.
Any potential failure with this approach ?
I typically include a project utilities file that contains a number of these things, just to make it easy. It functions as a pseudo global, but without many of the usual problems globals entail.
For example,
projectUtils.js
module.exports = {
initialize: function(next){
// initialization actions, there can be many of these
this.initializeDB(next);
},
initializeDb: function(next){
mongoClient.open(function(err, mongoClient) {
if(err) return next(err);
module.exports.db = mongoClient.db(DB);
next();
});
}
}
app.js
var projectUtils = require('projectUtils');
// (snip)
projectUtils.initialize(function(err) {
if(err) throw err; // bad DB initialization
// After this point and inside any of your routes,
// projectUtils.db is available for use.
app.listen(port);
}
By using an asynchronous initialize() function, you can be sure that all database connections, file I/O, etc., are done before starting up the server.
You can create a wrapper something like a provider and put it in provider.js, for instance.
Provider = function (db_name, host, port, username, password) {
var that = this;
var conn = generate_url(db_name, host, port, username, password); // you need to implement your version of generate_url()
MongoClient.connect(conn, function (err, db) {
if (err) {
throw err;
}
that.db = db;
});
};
//add the data access functions
Provider.prototype.getCollection = function (collectionName, callback) {
this.db.collection(collectionName, collectionOptions, callback);
};
exports.Provider = Provider;
This is how you use the provider:
var db = new Provider(db_name, host, port, username, password);
db.getCollection('collection name', callback);
how do i share the db object returned from when i call db.open or db.connect across the entire app?
i have a dbconnect.js module as follows :
var mongodb = require('mongodb');
var global_db = '';
// Define options. Note poolSize.
var serverOptions = {
'auto_reconnect': true,
'poolSize': 5
};
// Now create the server, passing our options.
var serv = new mongodb.Server('localhost', 27017, serverOptions);
// At this point, there is no connection made to the server.
// Create a handle to the Mongo database called 'myDB'.
var dbManager = new mongodb.Db('myDB', serv);
// NOW we initialize ALL 5 connections:
dbManager.open(function (error, db) {
// Do something with the connection.
global_db = db;
// Make sure to call db.close() when ALL connections need
// to be shut down.
db.close();
});
function getConnection()
{
return global_db;
}
exports.getConnection = getConnection;
and i am using this dbconnect.js in my app.js as:
var http = require('http');
var db = require('./dbconnect').getConnection();
var collection = db.collection('testcollection');
console.log(db);
console.log(collection);
var server = http.createServer();
server.on('request',route);
server.listen(8000,'127.0.0.1');
console.log('Server running at http://127.0.0.1:8000');
function route(request,response)
{
var url = request.url;
var doc = {};
doc[url] = 'ok';
collection.insert(doc,{w:1},function(err,result)
{
if(err) console.log(err);
else console.log(result);
});
}
in the console, the db and collection variable show empty values, i also tried removing the db.close() call in dbconnect.js but to no use, however the insertion works when i place it inside dbconnect.js file in the dbManager.open function, how do i do this?or any similar alternatives?
You can't do that, because dbManager.open( is async method, but you trying to get data from module synchronously.
Try this:
In dbconnect.js
var on_db_ready = null;
module.exports = {
db_ready:function(db_ready_callback){
on_db_ready = db_ready_callback;
//here we call callback if already have db
if (global_db) on_db_ready(global_db);
},
getConnection:getConnection
};
dbManager.open(function (error, db) {
if (on_db_ready) on_db_ready(db);
global_db= db;
})
in app.js:
var db = require('./dbconnect').db_ready(function(db){
//Here i have my database
//or can use getConnection method
});
this is not very beautiful way, but, I hope, explain your mistake
I'm a big Node.js and Mongo newbie, so please be gentle.
So here's my Node.js app:
var mongo = require('mongodb');
var Server = mongo.Server;
var Db = mongo.Db;
var server = new Server('hostname.mongolab.com', 666, {auto_reconnect : true}, {w:0, native_parser: false});
var db = new Db('dbName', server, {safe:true});
db.open(function(err, client) {
if(err) { return console.dir(err); }
client.authenticate('mongolabUser', 'mongolabUserPassword', function(authErr, success) {
if(authErr) { return console.dir(authErr); }
var stream = client.collection('myCollection').find({}).stream();
stream.on('data', function(item) {console.log("Do something with item"); });
stream.on('end', function() {console.log("Empty!");});
});
db.close();
});
Through prodigious use of debugger statements, I've come to the conclusion that the client.authenticate doesn't seem to be run. It looks like it's about to execute that line, but then just leapfrogs over it and goes straight to db.close().
But that's just the first of my problems. At some point prior, I was able to connect in to the database and authenticate, but my user was no retrieving anything in the find({}) command. I tried all sorts of ways, and streams are my latest attempt before deciding to give up on it for now.
Mongolab seems to be on v2.0.7, my mongo installation is v2.2.1. When I use the command line tool to log in as mongolabUser and execute a command like db.myCollection.find(), I get everything in my collection, so it can't be an issue with permissions.
Any advice/suggestions?
client.authenticate() is asynchronous, so the line that calls it starts the authentication, but doesn't wait for the server to respond before moving on to executing the next line, db.close(). So by the time the server responds the connection has been closed by the client.
Does moving the db.close() inside the event handler for stream.end help?
var mongo = require('mongodb');
var Server = mongo.Server;
var Db = mongo.Db;
var server = new Server('hostname.mongolab.com', 666, {auto_reconnect : true}, {w:0, native_parser: false});
var db = new Db('dbName', server, {safe:true});
db.open(function(err, client) {
if(err) { return console.dir(err); }
client.authenticate('mongolabUser', 'mongolabUserPassword', function(authErr, success) {
if(authErr) { return console.dir(authErr); }
var stream = client.collection('myCollection').find({}).stream();
stream.on('data', function(item) {console.log("Do something with item"); });
stream.on('end', function() {
console.log("Empty!");
db.close();
});
});
});
It is better to open a new connection or re-use ? when using module, because I'm used to separate my code into several files.
a.js
module.exports = function (req, res) {
new mongodb.... (err, db) { // open a connection
b(function (err, result) {
db.close(); // close the connection
res.send(result);
});
});
};
b.js
// re-open a connection ? or take the connection of "a.js" ? (passing "db")
When asynchronous, one must be careful to continue using the same connection (socket). This ensures that the next operation will not begin until after the write completes.
Thanks !
When you require('somemodule') and then require it again a second time, it will use the ALREADY loaded instance. This lets you create singletons quite easily.
So - inside of sharedmongo.js:
var mongo = require('mongodb');
// this variable will be used to hold the singleton connection
var mongoCollection = null;
var getMongoConnection = function(readyCallback) {
if (mongoCollection) {
readyCallback(null, mongoCollection);
return;
}
// get the connection
var server = new mongo.Server('127.0.0.1', 27017, {
auto_reconnect: true
});
// get a handle on the database
var db = new mongo.Db('squares', server);
db.open(function(error, databaseConnection) {
databaseConnection.createCollection('testCollection', function(error, collection) {
if (!error) {
mongoCollection = collection;
}
// now we have a connection
if (readyCallback) readyCallback(error, mongoCollection);
});
});
};
module.exports = getMongoConnection;
Then inside of a.js:
var getMongoConnection = require('./sharedmongo.js');
var b = require('./b.js');
module.exports = function (req, res) {
getMongoConnection(function(error, connection){
// you can use the Mongo connection inside of a here
// pass control to b - you don't need to pass the mongo
b(req, res);
})
}
And inside of b.js:
var getMongoConnection = require('./sharedmongo.js');
module.exports = function (req, res) {
getMongoConnection(function(error, connection){
// do something else here
})
}
The idea is when both a.js and b.js call getMongoCollection, the first time it will connect, and the second time it will return the already connected one. This way it ensure you are using the same connection (socket).
In the following code the function passed to the open function never runs, then the istruction console.log('open!') in the following code never runs:
var mongo = require("mongodb");
var Db = mongo.Db;
var connection = mongo.Connection;
var Server = mongo.Server;
var client = new Db('test', new Server("localhost", 27017, {}));
var test = function (err, collection) {
collection.insert({a:2}, function(err, docs) {
collection.count(function(err, count) {
test.assertEquals(1, count);
});
// Locate all the entries using find
collection.find().toArray(function(err, results) {
test.assertEquals(1, results.length);
test.assertTrue(results[0].a === 2);
// Let's close the db
client.close();
});
});
};
client.open(function(err, p_client) {
console.log('open!');
client.collection('test_insert', test);
});
From the log I see that the connection is accepted:
Sun March 11 16:52:01 [initandlisten] accepted connection from 127.0.0.1:61875 # 1
Mongodb great works from interactive shell.
can someone tell me any suggestion?
thank you!
Copy/paste this...it should work without any problems:
var client = new Db('test', new Server("localhost", 27017, {}), {});
client.open(function(err, client) {
console.log('open!');
}
Also, don't forget to authenticate after opening the connection.
var client = new Db('test', new Server("localhost", 27017, {}), {});
client.open(function(err, client) {
console.log('open!');
client.authenticate('admin', 'admin', function(err, result) {
// Authenticated
});
}