When using rewire and sinon fakeTimer, order matte

2019-05-20 17:13发布

While testing a setup with a timed interval, I came across this problem.

First of all, I am using sinon's fakeTimers to create the right timed environment. rewire is used as dependency injection library.

The problem is, that sometimes applying the fake timer seems to fail when rewire is involved while in some other instances it's perfectly working.

Please check out this setup:

test.js

'use strict';

require('should');
var sinon = require('sinon');
var rewire = require('rewire');

// this sample will not fall under the fake timer
var SampleGlobal = rewire('./testmodule');

describe('Sinon fake timer with rewirejs', function() {

    var clock;

    before(function() {
        clock = sinon.useFakeTimers();
    });

    after(function() {
        clock.restore();
    });

    it('work for locally rewired module', function() {

        var spy = sinon.spy();

        // locally inject-required module
        var Sample = rewire('./testmodule');

        new Sample().on('test', spy);

        spy.callCount.should.equal(0);

        clock.tick(5000);

        spy.callCount.should.equal(1);

    });

    it('break when rewired from global scope', function() {

        var spy = sinon.spy();

        // the module is globally inject-required
        new SampleGlobal().on('test', spy);

        spy.callCount.should.equal(0);

        clock.tick(5000);

        spy.callCount.should.equal(1);

    });

});

Now to include a second module with the interval:

testmodule.js

'use strict';

var EventEmitter = require('events').EventEmitter;
var util = require('util');

function Sample() {

    this.h = setInterval(this.emit.bind(this, 'test'), 5000);

}

util.inherits(Sample, EventEmitter);

module.exports = Sample;

Now, as you can see, the second test fails. This is the test that is using the module as it's required on top of the script (aka on the global scope). So I suspect it's because how rewire works and due to when the fakeTimers get installed.

Can anyone explain this in detail? Is there a way I can use inject-required modules with rewire on the global scope or do I always have to rewire them at the lower levels?

1条回答
Fickle 薄情
2楼-- · 2019-05-20 17:26

rewire prepends a list of var statements in order to import all globals into the local module scope. Thus you are able to change global variables without affecting other modules. The downside is that changes to global properties will now be shadowed by the local variables.

These pseudo-code snippets demonstrate the problem:

Without rewire

// Global scope
var setTimeout = global.setTimeout;

(function () {
    // Module scope. Node.js uses eval() and an IIFE to scope variables.
    ...
})()

With rewire

// Global scope
var setTimeout = global.setTimeout;

(function () {
    // Module scope.
    var setTimeout = global.setTimeout;
    ...
})()

You can solve your issue by rewiring after setting up sinon's fake timers.

查看更多
登录 后发表回答