Javascript stubing Date.now()和Math.random()

Javascript stubing Date.now()和Math.random(),javascript,node.js,unit-testing,dependency-injection,sinon,Javascript,Node.js,Unit Testing,Dependency Injection,Sinon,我使用with对node.js模块进行单元测试。我已经成功地模拟了其他依赖项(我编写的其他模块),但是我遇到了一些问题,比如stubing非纯函数(比如Math.random()和Date.now())。我尝试了以下方法(简化了,所以这个问题不太本地化),但是Math.random()由于明显的范围问题而没有被打断。Math的实例在测试文件和mymodule.js之间是独立的 test.js var sinon = require('sinon'), mymodule = requ

我使用with对node.js模块进行单元测试。我已经成功地模拟了其他依赖项(我编写的其他模块),但是我遇到了一些问题,比如stubing非纯函数(比如
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
        });
    });
});
var other = require('./other.js');

function funcThatDependsOnRandom(num) {
    if(typeof num === 'undefined') num = Math.random();

    return other.otherFunc(num);
}
因此,在这个精心设计的示例中,
functThatDependsOnRandom()
如下所示:

mymodule.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
        });
    });
});
var other = require('./other.js');

function funcThatDependsOnRandom(num) {
    if(typeof num === 'undefined') num = Math.random();

    return other.otherFunc(num);
}

在这种情况下,是否可以使用Sinon存根
Math.random()

你确定问题不在于模仿数学。这句话似乎没有多大意义:

sinon.mock(other).expects('otherFunc').withArgs(0.5).once();
您可以在一个模块中模拟其他模块,但在另一个模块中使用它。我认为你不会在
mymodule.js
中得到模拟版本。另一方面,stubing Math.random应该可以工作,因为这对于所有模块都是全局的


还可以看看这一点,了解nodeJS测试中的模拟依赖项。

是的,这是一个老问题,但它是有效的。这里有一个有效的答案,尽管我很想听听关于如何改进的建议

我在浏览器中处理这个问题的方法是创建一个代理对象。例如,不能在浏览器中存根窗口对象,因此可以创建名为windowProxy的代理对象。要获取位置,请在windowProxy中创建一个名为location的方法,该方法返回或设置windowLocation。然后,在测试时,模拟windowProxy.location

您可以对Node.js执行相同的操作,但它的工作方式并不简单。简单的版本是一个模块不能与另一个模块的私有名称空间相混淆

解决方案是使用该模块。初始化mockry之后,如果调用
require()
,调用的参数与您告诉mockry要模拟的参数相匹配,它将允许您重写require语句并返回您自己的属性

更新:我创建了一个功能完整的代码示例。它是开着的/结束更新

这些文档非常好,因此我建议阅读它们,但这里有一个示例:

创建一个包含以下内容的文件
randomHelper.js

module.exports.random = function() {
  return Math.random();
}
然后在需要随机数的代码中,您:

var randomHelper = require('./randomHelper');

console.log('A random number: ' + randomHelper.random() );
一切正常。代理对象的行为方式与Math.random相同

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

现在在您的测试中,(我使用的是摩卡和柴):


就这样。在这种情况下,我们的存根将始终返回0.0.99999;当然,您可以更改它。

使用
sinon
可以轻松地存根
Date.now()

假计时器提供一个时钟对象来传递时间,它也可以用来控制通过新日期()创建的日期对象;或者日期。现在();(如果浏览器支持)


如果我错了,请纠正我,但我认为模块是在第一次需要时缓存的。因此,
require('./other.js')
在测试套件和正在测试的代码中应该是同一个实例。基于这种想法,我假设(比如
Math.random
)在其中一个中模拟
other
,会修改另一个中的对象。但这可能不起作用,因为它会将新对象指定给其他对象,而不是替换属性。你知道西农有什么办法可以解决这个问题吗?回答得很好。你也可以用proxyquire代替mocky。