I am using jasmine-node to run tests against my nodejs functions. Being new to nodejs and mongodb, the first thing i ran into was testing some database calls, and I immediately got stuck, due to the asynchronous nature of nodejs.
What I want to do is:
1) Add an add
function to add new entries to a mongodb table
2) Receive a status string from that function to verify the action's status
The following is the code of my spec. In the beforeEach
call I initialise the database. As you can see in the implementation, it is only instantiated once, because of a condition asking if it already exists.
var mongo = require('../mongo.js');
describe('mongo', function() {
// generate a random number in order to test if the written item and the retrieved result match
var randomNumber = Math.random();
var item = {
'cities': {
'london': randomNumber
}
};
beforeEach(function() {
mongo.init();
waitsFor(function() {
return mongo.getCollection();
}, "should init the database", 10000);
});
it('should return "added" after adding an item to the database', function() {
var result;
waitsFor(function() {
result = mongo.add(item);
// the result value here is always undefined,
// due to the problem i'm having in my implementation
return result !== undefined;
}, "adding an item to the database", 10000);
runs(function() {
expect(result).toEqual('added');
});
});
});
Now, for every database query, I can define a callback function which is executed when the query has been run successfully. What I don't know how to achieve is delivering the result from the mongodb callback back the the spec.
This is the current implementation of the database functions:
var mongo = require('mongodb'),
Server = mongo.Server,
Db = mongo.Db;
var server = new Server('localhost', 27017, {auto_reconnect: true});
var db = new Db('exampleDb', server);
var collection = false;
// initialize database
var init = function() {
if (collection === false) {
db.open(dbOpenHandler);
}
};
var dbOpenHandler = function(err, db) {
db.collection('myCollection', dbCollectionHandler);
};
var dbCollectionHandler = function(err, coll) {
collection = coll;
};
/** returns the current db collection's status
* @return object db collection
*/
var getCollection = function() {
return collection !== false;
};
/** Add a new item to the database
* @param object item to be added
* @return string status code
*/
var add = function(item) {
var result = collection.insert( item, {safe: true}, function(err) {
// !! PROBLEM !!
// this return call returns the string back to the callee
// question: how would I return this as the add function's return value
return 'added';
});
};
// module's export functions
exports.init = init;
exports.getCollection = getCollection;
exports.add = add;
I'm also open for other approaches on how to test database calls in mongodb. I've read a bunch of articles about this topic, but none of them covers my particular case.
SOLUTION
Finally, and with the help of JohnnyHK's answer, I managed to make it work with a callback. Look at the following test case to understand what I did:
it('should create a new item', function() {
var response;
mongo.add(item, function( err, result) {
// set result to a local variable
response = result;
});
// wait for async call to be finished
waitsFor(function() {
return response !== undefined;
}, 'should return a status that is not undefined', 1000);
// run the assertion after response has been set
runs(function() {
expect(response).toEqual('added');
});
)}
You can do this much more cleanly now with the done
function in jasmine-node:
it('should create a new item', function(done) {
mongo.add(item, function(error, result) {
expect(result).toEqual('added');
done();
});
});
This test will wait until done()
is called asynchronously. There's a default timeout of 5 seconds, after which your test will fail. You can change this to, say, 15 seconds like so:
it('should create a new item', function(done) {
mongo.add(item, function(error, result) {
expect(result).toEqual('added');
done();
});
}, 15000);
You would have to change your add
method to accept a callback parameter so that it can deliver the asynchronous result to the caller via that callback:
var add = function(item, callback) {
collection.insert(item, {safe: true}, function(err) {
callback(err, 'added');
});
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With