未来所有模型实例的 Mongoose Save 方法的Sinon Mock(带有承诺)

Posted

技术标签:

【中文标题】未来所有模型实例的 Mongoose Save 方法的Sinon Mock(带有承诺)【英文标题】:Sinon Mock of Mongoose Save method for all future instances of a Model (with promises) 【发布时间】:2017-04-19 04:18:24 【问题描述】:

我正在尝试使用 Promise 在单元测试用例(Sinon、Mocha、Chai)期间为特定模型的所有实例模拟 Mongoose 保存方法。根据其他几个示例,我正在使用 sinon-mongoose 和 sinon-as-promised。我正在尝试达到类似测试代码的这种最终状态:

var expect = require('chai').expect;
var sinon  = require('sinon');
require('sinon-as-promised');
require('sinon-mongoose');

/* Mongoose schema+model for User method persist */
var UserModel = require('./models').userModel;
/* code module that will be tested */
var userMethods = require('./user');

/* unit test setup*/
var userModelMock = sinon.mock(UserModel);

/* mock all instances of UserModel saves with a forced error return to test code modules */        
userModelMock.expects('save')
         .chain('exec')
         .rejects('error saving');

/* call code module method for testing that creates new instance of UserModel and receives mocked save error*/
return userMethods.addUser().then(function(result)
    expect(result).to.equal(false);
    userModelMock.restore();
    );

我意识到 save 方法是每个实例的方法,因此上述模拟不能“全局”工作或在被测调用的 addUser() 方法中工作(addUser() 看不到模拟并命中数据库)。

是否有某种方法可以引用 Schema 对象或其他对象属性来模拟所有后续实例,而无需创建自定义对象包装器或使用其他深奥的方法?此 SO 帖子的最后一个答案(不是标记的答案)最接近,但它仅适用于模型的特定实例:Stubbing a Mongoose model using Sinon

【问题讨论】:

【参考方案1】:

我能够根据@Merott (Github) 的建议开发解决方案,如以下针对 sinon-mongoose 的问题讨论中所述(尽管有一些关于操纵原型的讨论):https://github.com/underscopeio/sinon-mongoose/issues/7

基本上我必须在保存模拟之前添加以下代码并处理模型原型:

Object.defineProperty(UserModel.prototype, 'save', 
  value: UserModel.prototype.save,
  configurable: true,
  );

上面有适当调整的完整代码sn-p:

var expect = require('chai').expect;
var sinon  = require('sinon');
require('sinon-as-promised');
require('sinon-mongoose');

/* Mongoose schema+model for User method persist */
var UserModel = require('./models').userModel;
/* code module that will be tested */
var userMethods = require('./user');

/* unit test setup*/
Object.defineProperty(UserModel.prototype, 'save', 
  value: UserModel.prototype.save,
  configurable: true,
);
var userModelMock = sinon.mock(UserModel.prototype);

/* mock all instances of UserModel saves with a forced error return to test code modules */        
userModelMock.expects('save')
     .chain('exec')
     .rejects('error saving');

/* call code module method for testing that creates new instance of UserModel and receives mocked save error*/
return userMethods.addUser().then(function(result)
    expect(result).to.equal(false);
    userModelMock.restore();
);

我看到的唯一问题是发布 Mock.restore()。如果我想通过 save() 返回正常的数据库调用,我在 mock restore() 后看到了一些问题。由于我正在模拟我所有的数据库调用,因此它并不相关,但对于那些需要混合模拟和真实调用的人来说可能是一个问题。

【讨论】:

“因为我在模拟我所有的数据库调用,所以它不相关,但对于那些需要混合模拟和真实调用的人来说,这可能是一个问题”。除非有人将单元/集成和 e2e 测试混为一谈,否则永远不会出现这种情况。

以上是关于未来所有模型实例的 Mongoose Save 方法的Sinon Mock(带有承诺)的主要内容,如果未能解决你的问题,请参考以下文章

当嵌入式数组对象更改时,Mongoose 实例 .save() 不起作用

猫鼬实例 .save() 不工作

猫鼬实例 .save() 不工作

无法使用 mongoose Model.save() 保存 - 给出内部服务器错误

Mongoose 在使用 .save() 方法更新文档时抛出 E11000 重复键错误

Mongoose - 如何防止 mongoose 在模型实例化时创建 _id