在模型上存入 mongoose save 方法
Stubbing the mongoose save method on a model
我想存根可用于 Mongoose 模型的 save
方法。这是一个示例模型:
/* model.js */
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
username: {
type: String,
required: true
}
});
var User = mongoose.model('User', userSchema);
module.exports = User;
我有一些辅助函数可以调用 save
方法。
/* 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);
});
};
我想使用单元测试检查 user.save
是否在我的辅助函数中被调用。
/* test.js */
var mongoose = require('mongoose');
var createUser = 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);
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)
});
我从 here 中发现了 var saveStub = sinon.stub(mongoose.Model.prototype, 'save')
。
一切都很好,除非我尝试向我的 saveStub
添加一些东西,例如saveStub.yields(null)
。如果我想用 saveStub.yields('mock error')
模拟传递给 save
回调的错误,我会收到此错误:
TypeError: Attempted to wrap undefined property undefined as function
堆栈跟踪完全没有帮助。
我做过的研究
我尝试按照建议重构我的模型以访问底层用户模型 here。这对我产生了同样的错误。这是我的尝试代码:
/* in model.js... */
var UserSchema = mongoose.model('User');
User._model = new UserSchema();
/* in test.js... */
var saveStub = sinon.stub(userModel._model, 'save');
我发现 this solution 根本不适合我。也许这是因为我以不同的方式设置了我的用户模型?
我也尝试过 this guide and this one 之后的 Mockery,但是这比我认为必要的设置要多得多,这让我质疑花时间隔离数据库的价值。
我的印象是,这一切都与 mongoose 实现的神秘方式有关 save
。我已经使用 npm hooks
阅读了一些关于它的内容,这使得 save
方法很难存根。
我也听说过 mockgoose,但我还没有尝试过该解决方案。有人成功地实施了该策略吗? [编辑:原来 mockgoose 提供了一个内存数据库以方便 setup/teardown,但它没有解决存根问题。]
任何有关如何解决此问题的见解都将不胜感激。
你试过了吗:
sinon.stub(userModel.prototype, 'save')
另外,测试中辅助函数在哪里被调用?看起来您将该函数定义为 utils 模块,但将其作为控制器对象的方法调用。我假设这与该错误消息无关,但它确实让我们更难确定调用存根的时间和地点。
这是我开发的最终配置,它使用了 sinon 和 mockery 的组合:
// Dependencies
var expect = require('chai').expect;
var sinon = require('sinon');
var mockery = require('mockery');
var reloadStub = require('../../../spec/utils/reloadStub');
describe('UNIT: userController.js', function() {
var reportErrorStub;
var controller;
var userModel;
before(function() {
// mock the error reporter
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
// load controller and model
controller = require('./userController');
userModel = require('./userModel');
});
after(function() {
// disable mock after tests complete
mockery.disable();
});
describe('#createUser', function() {
var req;
var res;
var status;
var end;
var json;
// Stub `#save` for all these tests
before(function() {
sinon.stub(userModel.prototype, 'save');
});
// Stub out req and res
beforeEach(function() {
req = {
body: {
username: 'Andrew',
userID: 1
}
};
status = sinon.stub();
end = sinon.stub();
json = sinon.stub();
res = { status: status.returns({ end: end, json: json }) };
});
// Reset call count after each test
afterEach(function() {
userModel.prototype.save.reset();
});
// Restore after all tests finish
after(function() {
userModel.prototype.save.restore();
});
it('should call `User.save`', function(done) {
controller.createUser(req, res);
/**
* Since Mongoose's `new` is asynchronous, run our expectations on the
* next cycle of the event loop.
*/
setTimeout(function() {
expect(userModel.prototype.save.callCount).to.equal(1);
done();
}, 0);
});
}
}
我想存根可用于 Mongoose 模型的 save
方法。这是一个示例模型:
/* model.js */
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
username: {
type: String,
required: true
}
});
var User = mongoose.model('User', userSchema);
module.exports = User;
我有一些辅助函数可以调用 save
方法。
/* 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);
});
};
我想使用单元测试检查 user.save
是否在我的辅助函数中被调用。
/* test.js */
var mongoose = require('mongoose');
var createUser = 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);
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)
});
我从 here 中发现了 var saveStub = sinon.stub(mongoose.Model.prototype, 'save')
。
一切都很好,除非我尝试向我的 saveStub
添加一些东西,例如saveStub.yields(null)
。如果我想用 saveStub.yields('mock error')
模拟传递给 save
回调的错误,我会收到此错误:
TypeError: Attempted to wrap undefined property undefined as function
堆栈跟踪完全没有帮助。
我做过的研究
我尝试按照建议重构我的模型以访问底层用户模型 here。这对我产生了同样的错误。这是我的尝试代码:
/* in model.js... */
var UserSchema = mongoose.model('User');
User._model = new UserSchema();
/* in test.js... */
var saveStub = sinon.stub(userModel._model, 'save');
我发现 this solution 根本不适合我。也许这是因为我以不同的方式设置了我的用户模型?
我也尝试过 this guide and this one 之后的 Mockery,但是这比我认为必要的设置要多得多,这让我质疑花时间隔离数据库的价值。
我的印象是,这一切都与 mongoose 实现的神秘方式有关 save
。我已经使用 npm hooks
阅读了一些关于它的内容,这使得 save
方法很难存根。
我也听说过 mockgoose,但我还没有尝试过该解决方案。有人成功地实施了该策略吗? [编辑:原来 mockgoose 提供了一个内存数据库以方便 setup/teardown,但它没有解决存根问题。]
任何有关如何解决此问题的见解都将不胜感激。
你试过了吗:
sinon.stub(userModel.prototype, 'save')
另外,测试中辅助函数在哪里被调用?看起来您将该函数定义为 utils 模块,但将其作为控制器对象的方法调用。我假设这与该错误消息无关,但它确实让我们更难确定调用存根的时间和地点。
这是我开发的最终配置,它使用了 sinon 和 mockery 的组合:
// Dependencies
var expect = require('chai').expect;
var sinon = require('sinon');
var mockery = require('mockery');
var reloadStub = require('../../../spec/utils/reloadStub');
describe('UNIT: userController.js', function() {
var reportErrorStub;
var controller;
var userModel;
before(function() {
// mock the error reporter
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
// load controller and model
controller = require('./userController');
userModel = require('./userModel');
});
after(function() {
// disable mock after tests complete
mockery.disable();
});
describe('#createUser', function() {
var req;
var res;
var status;
var end;
var json;
// Stub `#save` for all these tests
before(function() {
sinon.stub(userModel.prototype, 'save');
});
// Stub out req and res
beforeEach(function() {
req = {
body: {
username: 'Andrew',
userID: 1
}
};
status = sinon.stub();
end = sinon.stub();
json = sinon.stub();
res = { status: status.returns({ end: end, json: json }) };
});
// Reset call count after each test
afterEach(function() {
userModel.prototype.save.reset();
});
// Restore after all tests finish
after(function() {
userModel.prototype.save.restore();
});
it('should call `User.save`', function(done) {
controller.createUser(req, res);
/**
* Since Mongoose's `new` is asynchronous, run our expectations on the
* next cycle of the event loop.
*/
setTimeout(function() {
expect(userModel.prototype.save.callCount).to.equal(1);
done();
}, 0);
});
}
}