Writing the spec

With the server running, open your browser at http://0.0.0.0:8000/SpecRunner.html, to see the results of ours specs.

You can see that even though the server is running, and the spec appears to be correct, it is failing. It's due to the fact that stock.fetch() is asynchronous. A call to stock.fetch() returns immediately, allowing Jasmine to run the expectations before the AJAX request is completed:

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

To fix this, we need to embrace the asynchronicity of the stock.fetch() function and instruct Jasmine to wait for its execution before running the expectations.

The waitsFor() function

To tell Jasmine to wait for an asynchronous call, we need to use another of its global functions, waitsFor().

Before we can dig into how it works, let's jump ahead and adapt the previous test code to use this new function:

describe("when fetched", function() {
  var fetched = false;

  beforeEach(function() {
    stock.fetch({
      success: function () {
        fetched = true;
      }
    });

    waitsFor(function (argument) {
      return fetched;
    }, 'Timeout fetching stock data', 2000);
  });

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

The first thing you will notice is that we have added a success callback to the stock.fetch() function, to set the fetched variable to true after the fetch is complete:

stock.fetch({
  success: function () {
    fetched = true;
  }
});

Its implementation is as follows:

Stock.prototype.fetch = function(parameters) {
  var that = this;
  var params = parameters || {};
  var success = params.success || function () {};
  var url = 'http://0.0.0.0:8000/stocks/'+that.symbol;

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

Then we use the waitsFor() function to hold the execution of the it block, until the fetched variable is true:

waitsFor(function (argument) {
  return fetched;
}, 'Timeout fetching stock data', 2000);

And if the stock isn't fetched in 2000 milliseconds, it throws an error, making the spec fail.

Let's recap, the waitsFor() function accepts three parameters:

  • A function that Jasmine will poll, until it gets a truth result:

    function (argument) { return fetched; }

  • An error message to show if the waiting times out:

    "Timeout fetching stock data"

  • The amount of time (in milliseconds) it waits before timing out: 2000.

So whenever you have any expectations that depend on the result of an asynchronous call, you can hold its execution by using the waitsFor() function inside a beforeEach block.

Next, we will see how to use the waitsFor() function directly inside the it block.

The runs() function

We have seen that we can use the waitsFor() function inside beforeEach, but what if we need to write a test code that has an asynchronous call inside an it block?

As an exercise, let's rewrite the previous spec without nesting it in a describe block, but rather as single it block:

it("should be able to update its share price", function() {
  var fetched = false;

  stock.fetch({
    success: function() {
      fetched = true;
    }
  });

  waitsFor(function (argument) {
    return fetched;
  }, 'Timeout fetching stock data', 2000);

  expect(stock.sharePrice).toEqual(23.67);
});

By running this example, you will see that the problem of synchronism has come back. That is because the waitsFor() function is not blocking the execution.

It worked previously, because Jasmine waits to run the it block until waitsFor() has been completed.

So we need a way to schedule this expectation code to be run after the waitsFor() completes. As you might have guessed, it is going to be through another Jasmine global function, the runs function.

All you have to do is move the code that you want to respect the asynchronous behavior inside a runs block:

it("should be able to update its share price", function() {
  var fetched = false;

  stock.fetch({
    success: function() {
      fetched = true;
    }
  });

  waitsFor(function (argument) {
    return fetched;
  }, 'Timeout fetching stock data', 2000);

  runs(function() {
    expect(stock.sharePrice).toEqual(23.67);
  });
});

That way, Jasmine runs that code only after waitsFor() completes.

And you can even put multiple runs blocks, and they will run in the order they were declared:

it("should be able to update its share price", function() {
  var fetched = false;

  runs(function() {
    stock.fetch({
      success: function() {
        fetched = true;
      }
    });
  });

  waitsFor(function (argument) {
    return fetched;
  }, 'Timeout fetching stock data', 2000);

  runs(function() {
    expect(stock.sharePrice).toEqual(23.67);
  });
  runs(function() {
    expect(stock.sharePrice).not.toBeUndefined();
  });

  runs(function() {
    expect(stock.sharePrice).toBeGreaterThan(0);
  });
});
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.134.133