BackBone Model Unit Testing with Jasmine

Jasmine Unit Testing

Writing reliable JavaScript code at scale is difficult. The language lacks built-in formal structures that enable reliable engineering practices. Fortunately establishing convention and selecting mature libraries goes a long way towards building a trustworthy architecture. Furthermore writing unit tests gains confidence in our applications.

In this first article in a series on unit testing JavaScript web apps I’ll show Jasmine for testing an object inheriting from Backbone.Model. In particular we’ll review simulating service responses and exercising accessor functions.

Test App Repo

I wrote a sample app supporting this article by putting concepts into practice. If you want to get your hands on something and poke around feel free clone it from here:

https://github.com/KDawg/JasmineTestingBackBoneModel

Once you pull down that code running tests means opening this file in Chrome:

JasmineTestingBackBoneModel/tests/SpecRunner.html

Jasmine’s unit test reporting looks like:

BackboneModel Jasmine Unit Test Page

When you have a chance read through SpecRunner.html looking for how it organizes <script> tags bringing in dependent JavaScript libraries, app-specific code, and Jasmine support. It’s straight-forward with a few rules to abide success will be yours.

Jasmine Resources

I won’t try training you on Jasmine to any extent. If you already know it then cool, but otherwise check out these docs for more domain-specific knowledge.

What’s Up With Mock Data

Pretending to have a service response is far quicker than hitting the real end-point. Jasmine lets us short-circuit a Model.fetch() by immediately returning an object imitating an evaluated JSON response.

Is that important? It’s so important!

Slow tests create friction that programmers will route around. Avoiding running the test suite means tests aren’t maintained, new tests aren’t written, and submitted code is not automatically assured for passing regression.

Don’t get me wrong. So-called “integration tests” hitting the services with well-known user accounts and routes are totally worth coding. My warning is because they’re naturally slower so it’s worth figuring out how to segregate integration-tests into overnight batch jobs reported by your continuous-integration server.

Do you need a CI server? Have a look at Jenkins.

Great, now that you’re convinced that mocking out the service response makes unit tests respond more quickly, I’ll also claim that testing the models doesn’t require testing the service or the transmission mechanisms.

Organize Tests As If Reading a Story

You’ll see the Jasmine unit tests are hierarchically organized into larger suites and specs blocked by subject, and those are broken into detailed expectations. Reading them in a cascading flow appears like revealing narrative.

  • A Coffee Model
    • has property getting functions that
      • should return the name
  • A Coffee Model
    • when it fetches
      • should be able to parse mocked service response

At this point you might be wondering “what’s up with the coffee thing?” This example test is written around the defining model for my site “Made Fresh Coffee” where people virtually mix tasty ingredients, or pull from a complete recipe list, creating a virtual coffee sent to their favorite people.

Code Reading

Let’s go through code snippets of the example project seeing how Jasmine helps us unit test application code, and why I decided to do things a certain way.

Reading “Should be able to create its application test objects”

This is my convention kicking off a unit test assuring the simplest things are done. Creating the thing that needs testing and resources supporting that.

it('should be able to create its application test objects',
   function() {
      var coffee = new App.Model.Coffee();
      expect(coffee).toBeDefined();
      expect(MOCK_GET_DATA).toBeDefined();
      expect(MOCK_POST_DATA).toBeDefined();
});

In this case App.Model.Coffee is the model declared in the application’s global namespace. All of that code is brought into the test runner through various <script> tags written in SpecRunner.html.

MOCK_GET_DATA is an object defining what a coffee model definition looks like when calling to services. We can make these by looking at the “network” table in Chrome’s developer tools window while the app runs against a live service. Capture that output and write a mock data fixture.

var MOCK_GET_DATA = {
   author: 'Ken Tabor',
   ingredients: [18, 15, 1, 1, 1],
   message: 'Heya everyone, we have a crazy big deadline coming up but I know we can do it. Let\'s enjoy a coffee and finish strong!',
   name: 'Basic Drip',
   readUrl: 'http://www.madefreshcoffee.com/read.php?sku=bd86292a-241a-11e2-b97c-12313d04a24a'
};

Reading “Has Property Getter Functions That…”

Atop this code block an application object is created for testing following expectations.

var coffee = new App.Model.Coffee(MOCK_GET_DATA);

Reading “…Should Return the Name”

Calling a function on the app object returns a value that’s compared to what’s expected.

it('should return the name', function() {
   expect(coffee.getName()).toEqual('Basic Drip');
});

Reading “Has Property Setter Functions That…”

Creates its own application model for the suite of expectations following. Why bring in a new model? Just in case. Clean-room testing feels like the right thing to do most of the time. Realize that it() blocks can easily affect the test object creating side-effects rippling through down-stream expectations. Use your own judgement on this technique.

Reading “…Should Set the New Ingredients”

This block calls the model’s set function with an intended value, and then expects to find the proper values stored in the Backbone attribute. Why not use the app object’s corresponding .getIngredients() function? A fair question and we could do. Perhaps its just a matter of taste, but in this case I like the idea of testing more deeply by looking into the implementation details.

it('should set the new ingredients', function() {
   coffee.setIngredients([24, 22, 22, 9, 3]);
   expect(coffee.get('ingredients')).toEqual([24, 22, 22, 9, 3]);
});

Reading “When It Fetches”

Now we’re getting to a fun test. Jasmine “spies” are used switching away context from a JavaScript function for one of Jasmine’s own. Spies observe caller information and return with its own results (unit test) or passing through (integration test).

My example code shows one way of putting spies into action by short-circuiting Backbone.Model.fetch() calling to $.ajax() by returning MOCK_GET_DATA.

Jasmine beforeEach() and afterEach() statements are used in another attempt at being professional and scientific creating clean well-known test objects before each it() block. Jasmine will call beforeEach before each it() block and afterReach after each it() block. No surprises there.

describe('when it fetches', function() {
   var coffee;

   beforeEach(function() {
      spyOn($, 'ajax').andCallFake(function(options) {
         options.success(MOCK_GET_DATA);
      });
      coffee = new App.Model.Coffee();
      coffee.fetch();
   });

   afterEach(function() {
      coffee = undefined;
   });
});

Reading “should be able to parse mocked service response”

Granted this seems a bit procedural, but it confirms an assumption that the proper data is available by checking each Backbone.Model attribute expected from a service response.

it('should be able to parse mocked service response', function() {
   expect(_.isEmpty(coffee.attributes)).toEqual(false);
   expect(coffee.get('author')).toEqual('Ken Tabor');
   expect(coffee.get('ingredients')).toEqual([18, 15, 1, 1, 1]);
   expect(coffee.get('message')).toEqual('Heya everyone, we have a crazy big deadline coming up but I know we can do it. Let\'s enjoy a coffee and finish strong!');
   expect(coffee.get('name')).toEqual('Basic Drip');
   expect(coffee.get('readUrl')).toEqual('http://www.madefreshcoffee.com/read.php?sku=bd86292a-241a-11e2-b97c-12313d04a24a');
});

Your model might have a more complicated .parse() function perhaps synthesizing new attributes having unpacked and interpreted the service response. Given that case you can see where this is an even more interesting test.

More Tests Remain for Reading

Of course there are more tests in this sample spec. Please read them when you pull down the example code from my GitHub repo. The one checking $.ajax parameters is unique.

A Few of My Favorite Favorite Matchers

Do I actually have favorite matchers? In fact I do. Jasmine matchers compare a test object result with an expected value. I keep my toolbox lightweight assuring that what I use I use often and well.

  • .toEqual(‘booga booga’);
  • .toBeDefined();
  • .toBeUndefined();

Consider using and mastering just a few matchers to start. Dip into the Jasmine docs from time to time drawing up a few more features calling you. Incorporate them into your project when you’re feeling confident.

Realize that other Jasmine matcher libraries exist and you ought to explore those to use straight-up or serve as a jumping-off point for building your own. I particularly admire jasmine-jquery because it exposes semantic tests for DOM elements. Awesome stuff for my upcoming article on unit testing Backbone.View objects.

Further R&D Left to the Reader

My article is only the start of proper JavaScript unit testing. You’ll want to discover some of the following techniques for professional engineering:

  • Bring in a Jasmine “console reporter” for reporting text-only useful for Terminal or in a continuous-integration build process
  • GruntJS for localhost background watching and test auto-execute on source file change
  • Integration test – basically using .andCallThrough() instead of .andCallFake()
  • RequireJS and it’s impact on a testing framework
  • Seeking out, or rolling your own, Jasmine custom matchers

Take Advantage of Jasmine

Have a think on all of this. Unit testing using Jasmine is a wonderful way to power up the professional quality of your web app. Dynamic languages like JavaScript are nimble and flexible for us, but they don’t help craft fantastically stable application architecture.

Unit testing with Jasmine can build up your confidence level making programming life easier. Have a coffee and watch your application grow in a reliable manner.