Upgrading Jasmine from 1.3 to 2.1

Thursday, January 29th, 2015

Recently I upgraded the unit tests for a work project from using Jasmine 1.3 to Jasmine 2.1. The suite had about 400 tests. When I upgraded, only about 60 tests broke due to Jasmine API changes. Here are some of the differences I found between Jasmine 1.3 and Jasmine 2.1. The process took me about 2 hours. Hopefully this post will make the upgrade path a little faster for someone else.

Spy Changes

1. .andReturn()

spyOn(calculator, 'add').andReturn(5);
// now becomes
spyOn(calculator, 'add').and.returnValue(5);

2. spy.calls

expect(spy.calls[0].args[0]).toEqual(2);
// now becomes
expect(spy.calls.argsFor(0)[0]).toEqual(2);

3. .callCount

expect(spy.callCount).toEqual(1);
// now becomes
expect(spy.calls.count()).toEqual(1);

4. .andCallThrough()

var renderSpy = spyOn(view, 'render').andCallThrough();
// now becomes
var renderSpy = spyOn(view, 'render').and.callThrough();

5. .andCallFake()

spyOn(ProductAvailability.prototype, 'isAvailable').andCallFake(function() {});
// now becomes
spyOn(ProductAvailability.prototype, 'isAvailable').and.callFake(function() {});

6. spy.mostRecentCall

expect(spy.mostRecentCall.args[0]).toEqual(6);
// now becomes
expect(spy.calls.mostRecent().args[0]).toEqual(6);

7. .toThrow()

expect(function() {
  imageView.render();
}).toThrow('A basePath must be set.');

// now becomes

expect(function() {
  imageView.render();
}).toThrowError('A basePath must be set.');

Comparing Objects

When comparing objects in Jasmine 1.3, the following would pass:

expect({ name: 'David', bio: undefined }).toEqual({ name: 'David' });

Having a property bio set to undefined would be the same as not having the bio property at all. This no longer passes in Jasmine 2.1. Instead, the key must exist on both objects.

expect({ name: 'David', bio: undefined }).toEqual({ name: 'David', bio: undefined });

Asynchronous Testing

The change in the asynchronous testing API is one of the main reasons I wanted to upgrade Jasmine to 2.1. In Jasmine 1.3, you had to use runs() and waitsFor() to test asynchronous code. This was a little clunky, hard to remember, and made tests more difficult to understand.

it('should resolve with the Rewards object', function() {
  var flag = false;
  var resolveValue;

  runs(function() {
    var dfd = new $.Deferred();
    var promise = dfd.promise();

    dfd.resolve({ points: 5 });
    spyOn(Rewards.prototype, 'fetch').and.returnValue(promise);

    Rewards.get().then(function(rewards) {
      flag = true;
      resolveValue = rewards;
    });
  });

  waitsFor(function() {
    return flag;
  }, 'get should resolve with the model', 500);

  runs(function() {
    expect(resolveValue instanceof Rewards).toBe(true);
  });
});

In Jasmine 2.1, it works similar to asynchronous testing in Mocha. When done() is called, it signifies that this test has completed. Much less code and no more polling!

it('should resolve with the Rewards object', function(done) {
  var dfd = new $.Deferred();
  var promise = dfd.promise();

  dfd.resolve({ points: 5 });
  spyOn(Rewards.prototype, 'fetch').and.returnValue(promise);

  Rewards.get().then(function(rewards) {
    expect(rewards instanceof Rewards).toBe(true);
    done();
  });
});

Conclusion

These are just some of the differences I came across when upgrading from Jasmine 1.3 to 2.1. If there are others, please let me know in the comments and I will be sure to add them to this post.

Disclaimer: Any viewpoints and opinions expressed in this article are those of David Tang and do not reflect those of my employer or any of my colleagues.

comments powered by Disqus