Skip to content
This repository

jasmine.Clock.tick( 0 ) should not greedily call callbacks that get added while processing the call #289

Closed
thaustein opened this Issue · 1 comment

2 participants

Tobias Haustein Rajan Agaskar
Tobias Haustein

jasmine.Clock.tick(0) calls all callbacks that have previously been registered using setTimeout(). So far so good, but it also calls any callbacks which are added by the inital set of callbacks. This is not so good since it now is impossible to test that multiple callbacks are delayed in the JavaScript event loop instead of calling each other immediately.

I have code that must not call the callbacks immediately (since calling the callback immediately breaks a guarantee of the code; effectively the callback must not change any variables in the same event cycle). Therefore, the code uses setTimeout(callback1, 0) in order to defer the execution of the callback. The callback may use similar code with similar guarantees. Therefore, it also uses setTimeout(callback2, 0). Unluckily, Jasmine behaves differently for the first callback and for the second.

The problem is that in file mock-timeout.js in function jasmine.FakeTimer.prototype.runFunctionsWithinRange
the (next to) last line reads

jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillies) {
    // ...

        this.runFunctionsWithinRange(oldMillis, nowMillis);
    }
}

This line runs any newly added functions. In my opinion, this is simply wrong. At least, there should be an option (e.g. to jasmine.Clock.tick()) to disable this behavior.

This spec triggers the problem:

describe( "Jasmine should", function() {
    var callback1, callback2;

    beforeEach( function() {
        jasmine.Clock.useMock();

        callback1 = jasmine.createSpy( "callback1" ).andCallFake( function() {
            setTimeout( callback2, 0 );
        });

        callback2 = jasmine.createSpy( "callback2" );
    });

    it( "not call a scheduled function without calling Clock.tick() again", function() {
        setTimeout( callback1, 0 );
        expect( callback1 ).not.toHaveBeenCalled();
        expect( callback2 ).not.toHaveBeenCalled();
        jasmine.Clock.tick( 0 );
        expect( callback1 ).toHaveBeenCalled();
        expect( callback2 ).not.toHaveBeenCalled();
        jasmine.Clock.tick( 0 );
        expect( callback1 ).toHaveBeenCalled();
        expect( callback2 ).toHaveBeenCalled();
   });
});
Rajan Agaskar
Collaborator

I think this is expected behavior because it mirrors how setTimeout would behave in this scenario. The example you provide isn't enough to convince me that we need to add behavior to tick. Do you have a more involved example?

If you care about call order callback1 and callback2 can be real functions that make assertions.

describe( "Jasmine should", function() {
var callback1, callback2;

beforeEach( function() {
    jasmine.Clock.useMock();

    callback1 = jasmine.createSpy( "callback1" ).andCallFake( function() {
        setTimeout( callback2, 0 );
    });

    callback2 = function() {
      expect(callback1).toHaveBeenCalled();
    }
});

it( "not call a scheduled function without calling Clock.tick() again", function() {
    setTimeout( callback1, 0 );
    expect( callback1 ).not.toHaveBeenCalled();
    expect( callback2 ).not.toHaveBeenCalled();
    jasmine.Clock.tick( 0 );
    expect( callback1 ).toHaveBeenCalled();
 });

});

Rajan Agaskar ragaskar closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.