SHARE
Facebook X Pinterest WhatsApp

Test Asynchronous Methods Using the Jasmine runs() and waitFor() Methods

Written By
thumbnail
Rob Gravelle
Rob Gravelle
Apr 15, 2013

In the Testing Ajax Event Handlers using Jasmine Spies article, we saw how the use of Jasmine spies is an excellent way to test asynchronous Ajax calls where we don’t care about the practical implementation of the HttpXmlRequest or the network connection. However, there are times when that is precisely what you want to verify! In those instances, a better approach is to use the Jasmine runs() and waitFor() methods, which were both created especially for that purpose. In today’s article, we’ll take a look at some practical examples.

The runs() and waitFor() Methods Described

Both the runs() and waitFor() methods work in tandem in the testing of asynchronous processes.

Calls to run() methods act as individual testing blocks, the last of which would make the asynchronous call. In the simplest case, a setTimeout() can be used to simulate asynchronous behavior. In a more complex usage, an actual call would be made, such as to an Ajax-enabled object.

The waitsFor() method accepts three arguments:

  • a “latch” function
  • a failure message
  • a timeout (in milliseconds)

A latch function is not the same as a callback; whereas a callback executes once the asynchronous call returns, the latch function executes in 10ms intervals until it returns true or the timeout expires. In the event of a timeout, the spec fails with the provided error message. Hence, if the runs() function returns anything falsy, that does not terminate the test; it continues until the set timeout, which would be five seconds for the following example:

it("should simulate an asynchronous call", function () {
  runs(function() {
    flag = false;
    value = 0;

    setTimeout(function() {
      value++;
      //keep returning false…
      flag = false;
    }, 500);
  }); 

  waitsFor(function() {
    return flag;
  }, "The Value should be incremented", 5000); 
});
Ajax Tests Using run() and waitsFor() should simulate an asynchronous call.

timeout: timed out after 5000 msec waiting for The Value should be incremented

Once the asynchronous conditions have been met and waitsFor() returns true, another runs() block defines the final test behavior. This is usually expectations based on state after the asynchronous call returns. In the next example, setInterval() is utilized to mimic several call attempts, whereby the third one succeeds. Since the waitsFor() timeout is sufficiently generous, the test will pass. However, the final call to runs() will fail because value will equal 3 and not 4 as tested:

it("should simulate an asynchronous call", function () {
  runs(function() {
    flag = false;
    value = 0;
    intId = setInterval(function() {
      console.log(value);
      if (++value == 3) { clearInterval(intId); flag = true; }
    }, 500);
  }); 

  waitsFor(function() {
    return flag;
  }, "The Value should be incremented", 5000); 
  
  runs(function() {
    expect(value).toEqual(4); //this will fail
  });
});
Expected 3 to equal 4.

A shortfall of the above approach is its reliance on global variables. The absence of the var declaration keyword means that the flag, value, and intId variables are all created at the window level. A better solution is to declare the variables within the describe() method and then use the beforeEach() and afterEach() setup and teardown methods to initialize the variables and cancel setInterval(). In that case, the run() is not even required:

describe("Ajax Tests Using run() and waitsFor()", function() {
  var flag, value, intId;

  beforeEach(function() {
    flag = false,
    value = 0,
    intId = setInterval(function() {
      console.log(value);
      if (++value == 3) { flag = true; }
    }, 500);
  });

  afterEach(function() {
    clearInterval(intId); 
  });

  it("should simulate an asynchronous call", function () {
    waitsFor(function() {
      return flag;
    }, "The Value should be incremented", 5000); 
  });
});

Jasmine will call the runs() and waitFor() methods in the order you passed them. As soon as the JS parser gets to a waitFor() method it will poll it until it returns true and only then will it continue onto the next runs() method:

it("should simulate an asynchronous call", function () {
    waitsFor(function() {
      return value == 3;
    }, "The Value should be incremented", 5000); 
    
    runs(function() {
      expect(value).toEqual(3);
    });
    
    waitsFor(function() {
      return flag;
    }, "The Value should be incremented", 5000); 
    
    runs(function() {
      expect(flag).toEqual(true);
    });
  });

Making Real Ajax Requests

When it comes time to perform integration testing, you’ll probably want to execute real Ajax calls rather than just mimic them. No problem. Just create a spy, make the Ajax call, followed by waitsFor(), where you can check the callback.callCount property or what-have-you:

it("should make a real AJAX request", function () {
    var callback = jasmine.createSpy();
    makeAjaxCall(callback);
    waitsFor(function() {
        return callback.callCount > 0;
    }, "The Ajax call timed out.", 5000);
    
    runs(function() {
        expect(callback).toHaveBeenCalled();
    });
});

function makeAjaxCall(callback) {
    $.ajax({
        type: "GET",
        url: "data.json",
        contentType: "application/json; charset=utf-8"
        dataType: "json",
        success: callback
    });
}

Conclusion

Believe it or not, the jasmine testing framework is not straight-forward enough for some people, one of which is Derick Bailey. In an effort to make asynchronous processes even easier to test with jasmine he developed the Jasmine.Async library. Has he succeeded? Tune in later to find out…

Recommended for you...

The Revolutionary ES6 Rest and Spread Operators
Rob Gravelle
Aug 23, 2022
Ahead of Time (AOT) Compilation in Angular
Tariq Siddiqui
Aug 16, 2022
Converting a JavaScript Object to a String
Rob Gravelle
Aug 14, 2022
Understanding Primitive Type Coercion in JavaScript
Rob Gravelle
Jul 28, 2022
HTML Goodies Logo

The original home of HTML tutorials. HTMLGoodies is a website dedicated to publishing tutorials that cover every aspect of being a web developer. We cover programming and web development tutorials on languages and technologies such as HTML, JavaScript, and CSS. In addition, our articles cover web frameworks like Angular and React.JS, as well as popular Content Management Systems (CMS) that include WordPress, Drupal, and Joomla. Website development platforms like Shopify, Squarespace, and Wix are also featured. Topics related to solid web design and Internet Marketing also find a home on HTMLGoodies, as we discuss UX/UI Design, Search Engine Optimization (SEO), and web dev best practices.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.