我在使用sinon存根时遇到了一些问题,这可能源于我如何在我想要存根的模块上实现命名空间。直接在原型上定义的方法是我所期望的。
...my module.js
const Constructor = require('./constructor') //...just exports a singleton
/* Need to namespace some of my functions and retain the `this` context */
Object.defineProperty(Constructor.prototype, 'es', {
get: function() {
return {
method: require('./implementations/doesSomething.js').bind(this)
}
}
});
module.exports = Constructor;
/* ...testFile.js */
const Constructor = require('./constructor');
const instance = new Constructor();
const sinon = require('sinon');
sinon.stub(instance.es, 'method', function() {
return 'hijacked original method'
});
答案 0 :(得分:1)
正如前面提到的on the Sinon issue tracker,问题在于使用普通Object.defineProperty(obj, 'prop')
调用除了使用赋值(obj['prop'] = ...
)明确创建它之外还有其他功能。
通常,如果您尝试在没有Object.defineProperty
的情况下定义属性,那么它将是可存根的,但使用defineProperty
(不创建特殊配置)将无法存根该属性。原因很简单,default values for writeable
and configurable
是false
!你不能delete
他们或改变他们。如果你不能那样做,那么诗乃不会帮助你。因此,通常,您需要在属性定义中添加writeable: true, configurable: true
。
现在我还有一件事忘了回答:
您并没有尝试在Constructor.prototype.es.method
上包装函数 - 您尝试包装的是由es
属性上的getter返回的对象上的函数。这将从不工作。 为什么?只是因为返回的对象永远不会相同。您每次都在method
周围创建一个新对象。如果您确实需要替换/存根method
属性,则实际上需要替换整个Constructor.prototype.es
属性。如果你需要这个命名空间,你可以大大简化这个,并且还可以启用存根,如下所示:
Constructor.prototype.es = {};
Object.defineProperty(Constructor.prototype.es, 'method', {
get: function() {
return someFunction.bind(this);
},
writeable: true,
configurable:true
}
一个扩展的,完全有效的例子(Gist for download):
// constructor.js
const someFunction = function(){
return this.value;
}
function Constructor(){ };
Constructor.prototype.es = { value : 100 };
Object.defineProperty(Constructor.prototype.es, 'method', {
get: function() {
return someFunction.bind(this);
},
writeable: true,
configurable:true
});
// test.js
const instance = new Constructor();
console.log(instance.es.method()) // => 100
// using this won't work:
// sinon.stub(instance.__proto__.es, 'method').returns(42);
// because the getter is returning a _new_ function each time
// therefore you need to attack the actual getter function:
const stub = sinon.stub(instance.__proto__.es, 'method').value(()=>42);
console.log(instance.es.method()) // => 42
stub.get(()=>()=>84);
console.log(instance.es.method()) // => 84
stub.restore();
console.log(instance.es.method()) // => 100
// the above is working on the prototype, can't we do this on the instance?
// yes, we can, but remember that the `es` object is shared, so we
// can avoid modifying it by shadowing it further down the prototype
instance.es = { method: sinon.stub().returns(256) };
console.log(instance.es.method()) // => 256
delete instance.es
console.log(instance.es.method()) // => 100
<script src="https://unpkg.com/sinon@2.3.5/pkg/sinon.js"></script>