lundi 2 mars 2015

Stubbing the mongoose save method on a model

I would like to stub the save method available to Mongoose models. Here's a sample model:



/* model.js */
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
username: {
type: String,
required: true
}
});
var User = mongoose.model('User', userSchema);
module.exports = User;


I have some helper function that will call the save method.



/* utils.js */
var User = require('./model');
module.exports = function(req, res) {
var username = req.body.username;
var user = new User({ username: username });
user.save(function(err) {
if (err) return res.end();
return res.sendStatus(201);
});
};


I would like to check that user.save is called inside my helper function using a unit test.



/* test.js */
var mongoose = require('mongoose');
var utils = require('./utils');
var userModel = require('./model');

it('should do what...', function(done) {
var req = { username: 'Andrew' };
var res = { sendStatus: sinon.stub() };
var saveStub = sinon.stub(mongoose.Model.prototype, 'save');
saveStub.yields(null);

controller.createUser(req, res);

// because `save` is asynchronous, it has proven necessary to place the
// expectations inside a setTimeout to run in the next turn of the event loop
setTimeout(function() {
expect(saveStub.called).to.equal(true);
expect(res.sendStatus.called).to.equal(true);
done();
}, 0)
});


I discovered var saveStub = sinon.stub(mongoose.Model.prototype, 'save') from here.


All is fine unless I try to add something to my saveStub, e.g. with saveStub.yields(null). If I wanted to simulate an error being passed to the save callback with saveStub.yields('mock error'), I get this error:



TypeError: Attempted to wrap undefined property undefined as function


The stack trace is totally unhelpful.


The research I've done


I attempted to refactor my model to gain access to the underlying user model, as recommended here. That yielded the same error for me. Here was my code for that attempt:



/* in model.js... */
var UserSchema = mongoose.model('User');
User._model = new UserSchema();

/* in test.js... */
var saveStub = sinon.stub(userModel._model, 'save');


I found that this solution didn't work for me at all. Maybe this is because I'm setting up my user model in a different way?


I've also tried Mockery following this guide and this one, but that was way more setup than I thought should be necessary, and made me question the value of spending the time to isolate the db.


My impression is that it all has to do with the mysterious way mongoose implements save. I've read something about it using npm hooks, which makes the save method a slippery thing to stub.


I've also heard of mockgoose, though I haven't attempted that solution yet. Anyone had success with that strategy?


Any insight on how to resolve this issue would be very appreciated.


Aucun commentaire:

Enregistrer un commentaire