My node.js is using express framework along with Sails' waterline ORM. All of the model classes are exposed under global var "Model".
Here is my controller code.
module.exports = {
model: 'group', // If no model is specified, CRUD actions won't be inherited
actions: {
/**
* create group by owner
*
* @param {string} name group name
* @param {string} type group type
* @param {string} project id of the project the group belongs to optional
* @param {string} owner group owner id
*/
'post /create': function (req, res, next) {
var userId = req.param('owner');
var group = {
name: req.param('name'),
type: req.param('type'),
project: req.param('project'),
owner: req.param('owner'),
users: [{id: userId}]
};
Model.group.create(group, function(err, group){
if(err)
return res.json({status: 'error', data: err});
else{
//code update user.groups with newly created group
//should move to service class
Model.user.findOne(userId, function(err, user){
if(err)
console.log("can't find user with id: " + userId);
else{
user.groups.push({id: group.id});
user.save(function(err, re){
if(err)
console.log("update user.groups fails: " + user.id + " - " + group.id);
});
}
});
return res.json({status: 'success', data: group});
}
});
},
}
So how I test the controller code without test Model classes? It is unit test! ;)
As Java developer I used to use JMock or EasyMock to mock out the dependencies, or write little stub classes to stub out the dependencies. In node.js world there is something similar: http://sinonjs.org/docs/#sinonspy. Sinonjs is behavior driven testing framework. And with the help of the Mocha, the unit test framework for Node.js. This blog assumes you read the introduction of Mocha and Sinonjs.
So what I do is to stub out the Model classes and use Sinon spy to check the arguments passed back to respond obj.
Here is my little stub here can control the behavior of waterline models.
Model.group = {
create: function(group, callback) {callback(1, [])}
};
And another sub:
req.param = function(v) {return v;};
Now here comes the spy:
spy = res.json = sinon.spy();
The following is the complete test code:
var chai = require('chai');
var assert = require('assert');
var expect = chai.expect;
var group = require('../../../api/controllers/group.js');
Model = {};describe("create group", function() {
it("should generate error", function() {
//my little stub here can control the behavior of waterline models.
Model.group = {create: function(group, callback) {callback(1, [])}
};
req = res = next = {};
req.param = function(v) {return v;};
spy = res.json = sinon.spy();
group.actions['post /group/create'](req, res, next);
expect(spy.calledOnce);
assert(spy.args[0][0].status === 'error');
it("should generate success", function() {
Model.group = {create: function(group, callback) {callback(false, {name:'test'})}
};
findOne: function(user, callback) {callback(false, {groups:[], save: function(){}})}
};
req = res = next = {};
req.param = function(v) {return v;};
spy = res.json = sinon.spy();
group.actions['post /group/create'](req, res, next);
expect(spy.calledOnce);
assert(spy.args[0][0].status === 'success');
});
The wonderful thing about the dynamic programming language like javascript is that the functions are properties of object and passed around as reference. Note latest Java 8's lambda expression also has function reference. To create stub becomes extreme easy as demonstrated above.
Happy coding!
No comments:
Post a Comment