In the Chapter 4, Asynchronous Testing – AJAX, we have seen how including AJAX testing in the application can increase the complexity of the tests. In the example, we created a server where the results were predictable. It was basically a complex test fixture. And even though we could have used a real server implementation, it would increase the complexity of the test even more, try changing the state of a server with a database or third-party services from the browser, it is not an easy or scalable solution.
There is also the impact on productivity; these requests take time to process and to transmit, which hurts the quick feedback loop that unit testing usually provides.
You can also say that these specs are testing both the client and the server code, and therefore could not be considered as unit tests, but rather as integration tests.
A solution to all these problems is to use either Stubs or Fakes in place of the real dependencies of the code. So instead of making a request to the server, we use a test double of the server, inside the browser.
We are going to use the same example from Chapter 4, Asynchronous Testing – AJAX, and rewrite it using different techniques.
We have already seen some use cases for Jasmine spies. Remember that a spy is a special function that records how it was called. You can think of a Stub as a Spy with behavior.
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 revisit the example of the acceptance criteria, "Stock when fetched, should update its share price", by rewriting it using Jasmine Stubs.
Since we know that the stock's fetch
function is implemented using the $.getJSON
function:
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:
describe("when fetched", function() { beforeEach(function() { spyOn($, 'getJSON').andCallFake(function(url, callback) { callback({ sharePrice: 20.13 }); }); stock.fetch(); }); it("should update its share price", function() { expect(stock.sharePrice).toEqual(20.13); }); });
But this time we will use the andCallFake
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.13 }
).
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 the Sinon.JS library, is to Stub the browser's AJAX infrastructure instead, so the implementation of the AJAX request is free to be done in different manners.