Writing the spec

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

You can see that even though the server is running, and the spec appears to be correct, it is failing. This is 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(20.18);
});

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.

Asynchronous setups and teardowns

In the example shown, we invoke the fetch function during the spec's setup (the beforeEach function).

The only thing we need to do to identify that this setup step is asynchronous is add a done argument to its function definition:

describe("when fetched", function() {
  beforeEach(function(done) {
    
  });

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

Once Jasmine identifies this done argument, it passes as its value a function that must be called once the asynchronous operation is completed.

So we could then pass this done function as a success callback of the fetch function:

beforeEach(function(done) {
  stock.fetch({
    success: done
  });
});

At the implementation, invoke it once the AJAX operation is completed:

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

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

That is all there is to it; Jasmine will wait for the AJAX operation to be completed and the test will pass.

When required, it is also possible to have asynchronous afterEach definitions using the same done argument.

Asynchronous specs

Another approach would be to have an asynchronous spec instead of an asynchronous setup. To demonstrate how this would work, we are going to need to rewrite our previous acceptance criteria:

describe("Stock", function() {
  var stock;
  var originalSharePrice = 0;

  beforeEach(function() {
    stock = new Stock({
      symbol: 'AOUE',
      sharePrice: originalSharePrice
    });
  });

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

Again, all we have to do is add a done argument to its function definition and invoke the done function once the test is done:

it("should be able to update its share price", function(done) {
  stock.fetch({
    success: function () {
      expect(stock.sharePrice).toEqual(20.18);
      done();
    }
  });
});

The difference here is that we had to move the expectation for it to be inside the success callback right before invoking the done function.

Timeout

When writing asynchronous specs, Jasmine will wait for 5 seconds, by default, for the done callback to be called, failing the spec if it is not called before this timeout.

In this contrived example, where the server was a simple stub returning static data, that timeout was not a problem, but there are situations where that default time is not enough to complete an asynchronous task.

Although it is not recommended to have long-running specs, it is nice to know there is a way around this default behavior by changing a simple configuration variable in Jasmine called jasmine.DEFAULT_TIMEOUT_INTERVAL.

To make it take effect in the entire suite, one could set it at the SpecHelper.js file, as follows:

beforeEach(function() {
  jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

  jasmine.addMatchers({
    // matchers code
  });
});

jasmine.getFixtures().fixturesPath = 'spec/fixtures';

To make it take effect over a single spec, change its value in beforeEach and restore it during afterEach:

describe("Stock", function() {
  var defaultTimeout;

  beforeEach(function() {
    defaultTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
  });

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

  it("should be able to update its share price", function(done) {

  });
});
..................Content has been hidden....................

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