存根Date.now()和Math.random()

我正在用Sinon使用Mocha来testing我的node.js模块。 我已经成功地嘲笑了其他的依赖(我写的其他模块),但是我遇到了困扰非纯函数(如Math.random()Date.now() )的问题。 我已经尝试了以下(简化,以便这个问题不是如此本地化),但Math.random()不存在,因为一个明显的范围问题。 Math的实例在testing文件和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 }); }); }); 

所以在这个人为的例子中, functThatDependsOnRandom()将如下所示:

mymodule.js

 var other = require('./other.js'); function funcThatDependsOnRandom(num) { if(typeof num === 'undefined') num = Math.random(); return other.otherFunc(num); } 

是否有可能在这种情况下用Sinon存储Math.random()

是的,这是一个老问题,但它是有效的。 这是一个可行的答案,但我很乐意听取有关如何使其更好的build议。

我在浏览器中处理的方式是创build一个代理对象。 例如,不能在浏览器中存储窗口对象,以便创build名为windowProxy的代理对象。 当你想获得位置时,你在windowProxy中创build一个名为location的方法,返回或设置windowLocation。 然后,当testing时,你模拟windowProxy.location。

你可以用Node.js来做同样的事情,但是这并不简单。 简单的版本是一个模块不能混淆另一个模块的私有名称空间。

解决scheme是使用嘲笑模块。 在初始化嘲笑之后,如果你调用require()的参数来匹配你所嘲讽的模拟,那么它会让你覆盖require语句并返回你自己的属性。

更新:我创build了一个function完整的代码示例。 它在Github上是newz2000 / dice-tdd , 可以通过npm获得 。 /结束更新

文档非常好,所以我build议阅读它们,但这里有一个例子:

用这样的内容创build一个文件randomHelper.js

 module.exports.random = function() { return Math.random(); } 

然后在你需要一个随机数的代码中,你:

 var randomHelper = require('./randomHelper'); console.log('A random number: ' + randomHelper.random() ); 

一切都应该正常工作。 您的代理对象的行为与Math.random相同。

需要注意的是,require语句接受一个参数'./randomHelper' 。 我们需要注意的是。

现在在你的testing中,(我正在用摩卡和柴为例):

 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); } } 

就是这样。 在这种情况下,我们的存根将总是返回0.0.99999; 你当然可以改变它。

尝试:

  sinon.stub(Math, "random", function(){ return 0.5; }); 

你确定不嘲笑Math是问题吗? 看来这条线没什么意义:

 sinon.mock(other).expects('otherFunc').withArgs(0.5).once(); 

你在一个模块中嘲笑others ,但在另一个模块中使用它。 我不认为你会得到在mymodule.js嘲笑的版本。 另一方面,保存Math.random应该可以工作,因为这是所有模块的全局。

也看看这个在模拟NodeJStesting中的依赖关系。