Bai*_*ker 9 javascript unit-testing dependency-injection node.js sinon
我正在使用Mocha和Sinon对我的node.js模块进行单元测试.我已经成功地模拟了其他依赖项(我编写的其他模块),但是我遇到了存在非纯函数(如Math.random()和Date.now())的问题.我已经尝试了以下(简化以便这个问题不是如此本地化),但Math.random()由于明显的范围问题而没有被删除.实例Math在测试文件和mymodule.js.之间是独立的.
test.js
var sinon = require('sinon'),
mymodule = require('./mymodule.js'),
other = require('./other.js');
describe('MyModule', function() {
describe('funcThatDependsOnRandom', function() {
it('should call other.otherFunc with a random num when no num provided', function() {
sinon.mock(other).expects('otherFunc').withArgs(0.5).once();
sinon.stub(Math, 'random').returns(0.5);
funcThatDependsOnRandom(); // called with no args, so should call
// other.otherFunc with random num
other.verify(); // ensure expectation has been met
});
});
});
Run Code Online (Sandbox Code Playgroud)
所以在这个人为的例子中,functThatDependsOnRandom()看起来像:
mymodule.js
var other = require('./other.js');
function funcThatDependsOnRandom(num) {
if(typeof num === 'undefined') num = Math.random();
return other.otherFunc(num);
}
Run Code Online (Sandbox Code Playgroud)
Math.random()使用Sinon 可以在这种情况下存根吗?
是的,这是一个老问题,但它是有效的.这是一个有效的答案,但我很想听听如何让它变得更好的建议.
我在浏览器中处理这个问题的方法是创建一个代理对象.例如,您无法在浏览器中存根窗口对象,因此您可以创建名为windowProxy的代理对象.当您想要获取位置时,您在windowProxy中创建一个名为location的方法,该方法返回或设置windowLocation.然后,在测试时,您模拟windowProxy.location.
您可以使用Node.js执行相同的操作,但它不能简单地工作.简单的版本是一个模块不能弄乱另一个模块的私有命名空间.
解决方案是使用mockery模块.在初始化mockery之后,如果require()使用与mock告诉mock相匹配的参数调用,它将允许您覆盖require语句并返回自己的属性.
更新:我已经创建了一个功能齐全的代码示例.它位于github的newz2000/dice-tdd,可通过npm获得./ END UPDATE
文档非常好,所以我建议阅读它们,但这是一个例子:
创建一个randomHelper.js包含以下内容的文件:
module.exports.random = function() {
return Math.random();
}
Run Code Online (Sandbox Code Playgroud)
然后在需要随机数的代码中,您:
var randomHelper = require('./randomHelper');
console.log('A random number: ' + randomHelper.random() );
Run Code Online (Sandbox Code Playgroud)
一切都应该像平常一样工作.您的代理对象的行为方式与Math.random相同.
值得注意的是,require语句接受单个参数'./randomHelper'.我们需要注意这一点.
现在在你的测试中,(我正在使用mocha和chai):
var sinon = require('sinon');
var mockery = require('mockery')
var yourModule; // note that we didn't require() your module, we just declare it here
describe('Testing my module', function() {
var randomStub; // just declaring this for now
before(function() {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false
});
randomStub = sinon.stub().returns(0.99999);
mockery.registerMock('./randomHelper', randomStub)
// note that I used the same parameter that I sent in to requirein the module
// it is important that these match precisely
yourmodule = require('../yourmodule');
// note that we're requiring your module here, after mockery is setup
}
after(function() {
mockery.disable();
}
it('Should use a random number', function() {
callCount = randomStub.callCount;
yourmodule.whatever(); // this is the code that will use Math.random()
expect(randomStub.callCount).to.equal(callCount + 1);
}
}
Run Code Online (Sandbox Code Playgroud)
就是这样.在这种情况下,我们的存根将始终返回0.0.99999; 你当然可以改变它.