Ravit
Ravit

Reputation: 1532

Test asynchronous functionality in Jasmine 2.0.0 with done()

I am trying to implement a jasmine test on a simple promise implementation (asynchronous code) with the done() function and my test fails although the code being tested works perfectly fine. Can anyone please help me to figure out what is missing in my test?

 var Test = (function () {
    function Test(fn) {
        this.tool = null;
        fn(this.resolve.bind(this));
    }
    Test.prototype.then = function (cb) {
        this.callback = cb;
    };
    Test.prototype.resolve = function (value) {
        var me = this;
        setTimeout(function () {
            me.callback(value);
        }, 5000);
    };
    return Test;
})();

describe("setTimeout", function () {

    var test, newValue = false,
        originalTimeout;
    beforeEach(function (done) {
        originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
        jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

        test = new Test(function (cb) {
            setTimeout(function () {
                cb();
            }, 5000);
        });
        test.then(function () {
            newValue = true;
            console.log(1, newValue);
            done();
        });
    });

    it("Should be true", function (done) {
        expect(1).toBe(1);

        expect(newValue).toBeTruthy();
    });

    afterEach(function () {
        jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
    });
});

the same test in jsfiddle: http://jsfiddle.net/ravitb/zsachqpg/

Upvotes: 7

Views: 12719

Answers (1)

Lior
Lior

Reputation: 1618

This code is testing a simple promise like object, so will call the Test object a promise for convenience.

There are two different async events after the promise creation: 1. The call to the .then() method 2. The resolving of the promise by calling the cb() function in the beforeEach() function.

In the real world these two can be called in any order and at any time.

For the test, the .then() call must be moved to the it() section's callback and all spec methods (e.g expect()) need to be called in it's callback or they'll run before it's resolved. The beforeEach() is part of the test setup while the it() function is the spec, the test itself.

The done() method needs to be called twice,

  1. When the beforeEach() async action is finished (i.e after the cb() is called), that will start running the spec. So it should look something like this:

      beforeEach(function (done) {
        test = new Test(function (cb) {
            setTimeout(function () {
                console.log("in beforeEach() | setTimeout()");
                cb(resolvedValue);
                done()
            }, 500);
        });
      });
    
  2. When the spec's (it() section's) async action is finished inside the .then() method after all calls to jasmine test methods, this will tell Jasmine the spec finished running (and so the time-out won't be reached). So:

    it("Should be " + resolvedValue, function (done) {
        test.then(function (value) {
            console.log("in then()");
            expect(value).toBe(resolvedValue);
            done();
        });      
    });
    

Also, as you can see instead of testing that a variable's value has changed I'm testing that the value passed to the .then() method is the same as the one passed to the promise resolve cb() function as that is the right behaviour you are expecting.

Here's an updated version of your fiddle.

You can check in the browser's console to see that all callbacks are being called

Note: Changing Jasmine's DEFAULT_TIMEOUT_INTERVAL just makes it more convoluted for no reason, so I removed it and some extraneous code.

Upvotes: 7

Related Questions