簡體   English   中英

Stubbing Date.now()和Math.random()

[英]Stubbing Date.now() and Math.random()

我正在使用MochaSinon對我的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
        });
    });
});

所以在這個人為的例子中, 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()

是的,這是一個老問題,但它是有效的。 這是一個有效的答案,但我很想聽聽如何讓它變得更好的建議。

我在瀏覽器中處理這個問題的方法是創建一個代理對象。 例如,您無法在瀏覽器中存根窗口對象,因此您可以創建名為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();
}

然后在需要隨機數的代碼中,您:

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

console.log('A random number: ' + randomHelper.random() );

一切都應該像平常一樣工作。 您的代理對象的行為方式與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);
  }
}

就是這樣。 在這種情況下,我們的存根將始終返回0.0.99999; 你當然可以改變它。

你確定沒有嘲笑Math就是問題。 似乎這條線沒有多大意義:

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

你在一個模塊中嘲笑others ,但在另一個模塊中使用它。 我不認為你會在mymodule.js獲得模擬版本。 另一方面,存在Math.random應該可以工作,因為這對所有模塊都是全局的。

另請參閱此SO以模擬nodeJS測試中的依賴項。

使用偽定時器很容易使用sinonDate.now()存根:

偽定時器提供時鍾對象來傳遞時間,也可以用來控制通過新的Date()創建的Date對象; 或Date.now(); (如果瀏覽器支持)。

// Arrange
const now = new Date();
const clock = sinon.useFakeTimers(now.getTime());

// Act
// Call you function ...

// Assert
// Make some assertions ...

// Teardown
clock.restore();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM