5 min read

In this article by Paulo Ragonha, author of the book Jasmine JavaScript Testing – Second Edition, we will learn Jasmine stubs and Jasmine Ajax plugin.

(For more resources related to this topic, see here.)

Jasmine stubs

We use stubs whenever we want to force a specific path in our specs or replace a real implementation for a simpler one.

Let’s take the example of the acceptance criteria, “Stock when fetched, should update its share price”, by writing it using Jasmine stubs.

The stock’s fetch function is implemented using the $.getJSON function, as follows:

Stock.prototype.fetch = function(parameters) {
$.getJSON(url, function (data) {
   that.sharePrice = data.sharePrice;
   success(that);
});
};

We could use the spyOn function to set up a spy on the getJSON function with the following code:

describe("when fetched", function() {
beforeEach(function() {
   spyOn($, 'getJSON').and.callFake(function(url, callback) {
     callback({ sharePrice: 20.18 });
   });
   stock.fetch();
});
 
it("should update its share price", function() {
   expect(stock.sharePrice).toEqual(20.18);
});
});

We will use the and.callFake function to set a behavior to our spy (by default, a spy does nothing and returns undefined). We make the spy invoke its callback parameter with an object response ({ sharePrice: 20.18 }).

Later, at the expectation, we use the toEqual assertion to verify that the stock’s sharePrice has changed.

To run this spec, you no longer need a server to make the requests to, which is a good thing, but there is one issue with this approach. If the fetch function gets refactored to use $.ajax instead of $.getJSON, then the test will fail. A better solution, provided by a Jasmine plugin called jasmine-ajax, is to stub the browser’s AJAX infrastructure instead, so the implementation of the AJAX request is free to be done in different manners.

Jasmine Ajax

Jasmine Ajax is an official plugin developed to help out the testing of AJAX requests. It changes the browser’s AJAX request infrastructure to a fake implementation.

This fake (or mocked) implementation, although simpler, still behaves like the real implementation to any code using its API.

Installing the plugin

Before we dig into the spec implementation, first we need to add the plugin to the project. Go to https://github.com/jasmine/jasmine-ajax/ and download the current release (which should be compatible with the Jasmine 2.x release). Place it inside the lib folder.

It is also needed to be added to the SpecRunner.html file, so go ahead and add another script:

<script type="text/javascript" src="lib/mock-ajax.js"></script>

A fake XMLHttpRequest

Whenever you are using jQuery to make AJAX requests, under the hood it is actually using the XMLHttpRequest object to perform the request.

XMLHttpRequest is the standard JavaScript HTTP API. Even though its name suggests that it uses XML, it supports other types of content such as JSON; the name has remained the same for compatibility reasons.

So, instead of stubbing jQuery, we could change the XMLHttpRequest object with a fake implementation. That is exactly what this plugin does.

Let’s rewrite the previous spec to use this fake implementation:

describe("when fetched", function() {
beforeEach(function() {
   jasmine.Ajax.install();
});
 
beforeEach(function() {
   stock.fetch();
 
   jasmine.Ajax.requests.mostRecent().respondWith({
     'status': 200,
     'contentType': 'application/json',
     'responseText': '{ "sharePrice": 20.18 }'
   });
});
 
afterEach(function() {
   jasmine.Ajax.uninstall();
});
 
it("should update its share price", function() {
   expect(stock.sharePrice).toEqual(20.18);
});
});

Drilling the implementation down:

  1. First, we tell the plugin to replace the original implementation of the XMLHttpRequest object by a fake implementation using the jasmine.Ajax.install function.
  2. We then invoke the stock.fetch function, which will invoke $.getJSON, creating XMLHttpRequest anew under the hood.
  3. And finally, we use the jasmine.Ajax.requests.mostRecent().respondWith function to get the most recently made request and respond to it with a fake response.

We use the respondWith function, which accepts an object with three properties:

  1. The status property to define the HTTP status code.
  2. The contentType (JSON in the example) property.
  3. The responseText property, which is a text string containing the response body for the request.

Then, it’s all a matter of running the expectations:

it("should update its share price", function() {
expect(stock.sharePrice).toEqual(20.18);
});

Since the plugin changes the global XMLHttpRequest object, you must remember to tell Jasmine to restore it to its original implementation after the test runs; otherwise, you could interfere with the code from other specs (such as the Jasmine jQuery fixtures module). Here’s how you can accomplish this:

afterEach(function() {
jasmine.Ajax.uninstall();
});

There is also a slightly different approach to write this spec; here, the request is first stubbed (with the response details) and the code to be exercised is executed later.

The previous example is changed to the following:

beforeEach(function() {
jasmine.Ajax.stubRequest('http://localhost:8000/stocks/AOUE').andReturn({
   'status': 200,    'contentType': 'application/json',    'responseText': '{ "sharePrice": 20.18 }' });   stock.fetch(); });

It is possible to use the jasmine.Ajax.stubRequest function to stub any request to a specific request. In the example, it is defined by the URL http://localhost:8000/stocks/AOUE, and the response definition is as follows:

{
'status': 200,
'contentType': 'application/json',
'responseText': '{ "sharePrice": 20.18 }'
}

The response definition follows the same properties as the previously used respondWith function.

Summary

In this article, you learned how asynchronous tests can hurt the quick feedback loop you can get with unit testing. I showed how you can use either stubs or fakes to make your specs run quicker and with fewer dependencies.

We have seen two different ways in which you could test AJAX requests with a simple Jasmine stub and with the more advanced, fake implementation of the XMLHttpRequest.

You also got more familiar with spies and stubs and should be more comfortable using them in different scenarios.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here