在模型上存储mongoose save方法

And*_*paw 10 unit-testing mocha.js mongoose

我想将saveMongoose模型可用的方法存根.这是一个示例模型:

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

我有一些将调用该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);
  });
};
Run Code Online (Sandbox Code Playgroud)

我想检查一下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)
});
Run Code Online (Sandbox Code Playgroud)

var saveStub = sinon.stub(mongoose.Model.prototype, 'save')这里发现.

一切都很好,除非我尝试添加一些东西给我saveStub,例如saveStub.yields(null).如果我想模拟传递给save回调的错误saveStub.yields('mock error'),我会收到此错误:

TypeError: Attempted to wrap undefined property undefined as function
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪完全没有用.

我做过的研究

我试图重构我的模型来访问底层的用户模型,推荐这里.这给我带来了同样的错误.这是我尝试的代码:

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

/* in test.js... */
var saveStub = sinon.stub(userModel._model, 'save');
Run Code Online (Sandbox Code Playgroud)

我发现这个解决方案对我来说根本不起作用.也许这是因为我以不同的方式设置我的用户模型?

我也试过以下嘲弄本指南这一个,但比我想象中应该是必要的,让我质疑花费隔离分贝的时间价值的方式更多的设置.

我的印象是,这一切都与猫鼬实现的神秘方式有关save.我已经使用npm阅读了一些关于它的内容hooks,这使得该save方法很难实现.

我也听说过mockgoose,虽然我还没有尝试过这个解决方案.任何人都有这个策略的成功?[编辑:事实证明,mockgoose提供了一个内存数据库,易于设置/拆卸,但它无法解决存根问题.]

任何有关如何解决此问题的见解将非常感激.

And*_*paw 8

这是我开发的最终配置,它使用了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);
    });
  }
}
Run Code Online (Sandbox Code Playgroud)


小智 6

你有没有尝试过:

sinon.stub(userModel.prototype, 'save')
Run Code Online (Sandbox Code Playgroud)

另外,在测试中在哪里调用辅助函数?看起来您将函数定义为 utils 模块,但将其作为控制器对象的方法调用。我假设这与该错误消息无关,但它确实使确定存根何时何地被调用变得更加困难。