如何使用 sinon.js 在 nodejs 中存根全局依赖项的新实例方法
How stub a global dependency's new instance method in nodejs with sinon.js
抱歉,标题令人困惑,我不知道如何更好地描述它。来看代码:
var client = require('some-external-lib').createClient('config string');
//constructor
function MyClass(){
}
MyClass.prototype.doSomething = function(a,b){
client.doWork(a+b);
}
MyClass.prototype.doSomethingElse = function(c,d){
client.doWork(c*d);
}
module.exports = new MyClass();
测试:
var sinon = require('sinon');
var MyClass = requre('./myclass');
var client = require('some-external-lib').createClient('config string');
describe('doSomething method', function() {
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork');
MyClass.doSomething();
assert(stub.calledOnce); //not working! returns false
})
})
如果在每个方法中调用 .createClient('xxx') 我可以让它工作,我在其中存根客户端:
var client = require('some-external-lib');
sinon.stub(client, 'createClient').returns({doWork:function(){})
但是每次调用方法时都初始化客户端感觉不对。
有没有更好的方法对上面的代码进行单元测试?
NEW:我创建了一个最小的工作演示来演示我的意思:https://github.com/markni/Whosebug30825202(只需 npm install && npm test
,观察测试失败。)这问题求解决方案,不改主代码就可以通过
您可以使用 beforeEach() 和 afterEach() 挂钩来存根全局依赖项。
var sinon = require('sinon');
var MyClass = requre('./myclass');
var client = require('some-external-lib').createClient('config string');
describe('doSomething method', function() {
beforeEach(function () {
// Init global scope here
sandbox = sinon.sandbox.create();
});
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork').yield();
MyClass.doSomething();
assert(stub.calledOnce); //not working! returns false
})
afterEach(function () {
// Clean up global scope here
sandbox.restore();
});
})
部分问题在这里:var stub = sinon.stub(client,'doWork').yield();
yield
不是 return 存根。此外,yield
期望已使用回调参数调用存根。
否则,我认为你已经完成了 95%。您可以简单地删除存根,而不是为每个测试重新初始化:
describe('doSomething method', function() {
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork');
MyClass.doSomething();
assert(stub.calledOnce);
stub.restore();
})
})
顺便说一句,另一位发帖人建议使用 Sinon 沙箱,这是一种自动删除存根的便捷方法。
问题出在测试定义的地方。事实上,在 Node.js 中很难进行依赖注入。在针对您的答案进行研究时,我遇到了一个有趣的 article,其中 DI 是通过自定义 loadmodule
函数实现的。这是一个相当复杂的解决方案,但也许最终您会遇到它,所以我认为值得一提。除了 DI 之外,它还提供了访问测试模块的私有变量和函数的好处。
要解决您问题中描述的直接问题,您可以存根 some-external-lib
模块的客户端创建方法。
var sinon = require('sinon');
//instantiate some-external-lib
var client = require('some-external-lib');
//stub the function of the client to create a mocked client
sinon.stub(client, 'createClient').returns({doWork:function(){})
//due to singleton nature of modules `require('some-external-lib')` inside
//myClass module will get the same client that you have already stubbed
var MyClass = require('./myclass');//inside this your stubbed version of createClient
//will be called.
//It will return a mock instead of a real client
但是,如果您的测试变得更加复杂并且模拟客户端获得状态,您将不得不手动处理重置不同单元测试之间的状态。您的测试应该独立于它们的启动顺序。这是重置 beforeEach
部分
中所有内容的最重要原因
抱歉,标题令人困惑,我不知道如何更好地描述它。来看代码:
var client = require('some-external-lib').createClient('config string');
//constructor
function MyClass(){
}
MyClass.prototype.doSomething = function(a,b){
client.doWork(a+b);
}
MyClass.prototype.doSomethingElse = function(c,d){
client.doWork(c*d);
}
module.exports = new MyClass();
测试:
var sinon = require('sinon');
var MyClass = requre('./myclass');
var client = require('some-external-lib').createClient('config string');
describe('doSomething method', function() {
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork');
MyClass.doSomething();
assert(stub.calledOnce); //not working! returns false
})
})
如果在每个方法中调用 .createClient('xxx') 我可以让它工作,我在其中存根客户端:
var client = require('some-external-lib');
sinon.stub(client, 'createClient').returns({doWork:function(){})
但是每次调用方法时都初始化客户端感觉不对。
有没有更好的方法对上面的代码进行单元测试?
NEW:我创建了一个最小的工作演示来演示我的意思:https://github.com/markni/Whosebug30825202(只需 npm install && npm test
,观察测试失败。)这问题求解决方案,不改主代码就可以通过
您可以使用 beforeEach() 和 afterEach() 挂钩来存根全局依赖项。
var sinon = require('sinon');
var MyClass = requre('./myclass');
var client = require('some-external-lib').createClient('config string');
describe('doSomething method', function() {
beforeEach(function () {
// Init global scope here
sandbox = sinon.sandbox.create();
});
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork').yield();
MyClass.doSomething();
assert(stub.calledOnce); //not working! returns false
})
afterEach(function () {
// Clean up global scope here
sandbox.restore();
});
})
部分问题在这里:var stub = sinon.stub(client,'doWork').yield();
yield
不是 return 存根。此外,yield
期望已使用回调参数调用存根。
否则,我认为你已经完成了 95%。您可以简单地删除存根,而不是为每个测试重新初始化:
describe('doSomething method', function() {
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork');
MyClass.doSomething();
assert(stub.calledOnce);
stub.restore();
})
})
顺便说一句,另一位发帖人建议使用 Sinon 沙箱,这是一种自动删除存根的便捷方法。
问题出在测试定义的地方。事实上,在 Node.js 中很难进行依赖注入。在针对您的答案进行研究时,我遇到了一个有趣的 article,其中 DI 是通过自定义 loadmodule
函数实现的。这是一个相当复杂的解决方案,但也许最终您会遇到它,所以我认为值得一提。除了 DI 之外,它还提供了访问测试模块的私有变量和函数的好处。
要解决您问题中描述的直接问题,您可以存根 some-external-lib
模块的客户端创建方法。
var sinon = require('sinon');
//instantiate some-external-lib
var client = require('some-external-lib');
//stub the function of the client to create a mocked client
sinon.stub(client, 'createClient').returns({doWork:function(){})
//due to singleton nature of modules `require('some-external-lib')` inside
//myClass module will get the same client that you have already stubbed
var MyClass = require('./myclass');//inside this your stubbed version of createClient
//will be called.
//It will return a mock instead of a real client
但是,如果您的测试变得更加复杂并且模拟客户端获得状态,您将不得不手动处理重置不同单元测试之间的状态。您的测试应该独立于它们的启动顺序。这是重置 beforeEach
部分