Stubbing virtual attributes of Mongoose model

强颜欢笑 提交于 2019-12-11 02:27:34

问题


Is there a way to stub a virtual attribute of a Mongoose Model?

Assume Problem is a model class, and difficulty is a virtual attribute. delete Problem.prototype.difficulty returns false, and the attribute is still there, so I can't replace it with any value I want.

I also tried

var p = new Problem();
delete p.difficulty;
p.difficulty = Problem.INT_EASY;

It didn't work.

Assigning undefined to Problem.prototype.difficulty or using sinon.stub(Problem.prototype, 'difficulty').returns(Problem.INT_EASY); would throw an exception "TypeError: Cannot read property 'scope' of undefined", while doing

  var p = new Problem();
  sinon.stub(p, 'difficulty').returns(Problem.INT_EASY);

would throw an error "TypeError: Attempted to wrap string property difficulty as function".

I am running out of ideas. Help me out! Thanks!


回答1:


mongoose internally uses Object.defineProperty for all properties. Since they are defined as non-configurable, you can't delete them, and you can't re-configure them, either.

What you can do, though, is overwriting the model’s get and set methods, which are used to get and set any property:

var p = new Problem();
p.get = function (path, type) {
  if (path === 'difficulty') {
    return Problem.INT_EASY;
  }
  return Problem.prototype.get.apply(this, arguments);
};

Or, a complete example using sinon.js:

var mongoose = require('mongoose');
var sinon = require('sinon');

var problemSchema = new mongoose.Schema({});
problemSchema.virtual('difficulty').get(function () {
  return Problem.INT_HARD;
});

var Problem = mongoose.model('Problem', problemSchema);
Problem.INT_EASY = 1;
Problem.INT_HARD = 2;

var p = new Problem();
console.log(p.difficulty);
sinon.stub(p, 'get').withArgs('difficulty').returns(Problem.INT_EASY);
console.log(p.difficulty);



回答2:


As of the end of 2017 and the current Sinon version, stubbing only some of the arguments (e.g. only virtuals on mongoose models) can be achieved in the following manner

  const ingr = new Model.ingredientModel({
    productId: new ObjectID(),
  });

  // memorizing the original function + binding to the context
  const getOrigin = ingr.get.bind(ingr);

  const getStub = S.stub(ingr, 'get').callsFake(key => {

    // stubbing ingr.$product virtual
    if (key === '$product') {
      return { nutrition: productNutrition };
    }

    // stubbing ingr.qty
    else if (key === 'qty') {
      return { numericAmount: 0.5 };
    }

    // otherwise return the original 
    else {
      return getOrigin(key);
    }

  });

The solution is inspired by a bunch of different advises, including one by @Adrian Heine



来源:https://stackoverflow.com/questions/18412606/stubbing-virtual-attributes-of-mongoose-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!