Javascript Testing: Units; Backbone Models and Views










Javascript Testing:
Units; Backbone Models and Views

or, How To Avoid Race Conditions and Live Longer










The State of the Art

QUnit

jQuery's Test Suite

QUnit

      test("a basic test example", function() {
        ok( true, "this test is fine" );
        var value = "hello";
        equal( value, "hello", "We expect value to be hello" );
      });

      module("Module A");

      test("first test within module", function() {
        ok( true, "all pass" );
      });

      test("second test within module", function() {
        ok( true, "all pass" );
      });
    

QUnit

Jasmine

Javascript BDD

Jasmine

      describe("Jasmine", function() {
        it("makes testing JavaScript awesome!", function() {
          expect(yourCode).toBeLotsBetter();
        });
        it("uses natural language syntax", function() {
            expect(true).toBeTruthy();
        });
      });
    

Jasmine

JsUnit

Grandfather Time

Sinon.js

Black Magic

Sinon.js

      "test should call mocked subscriber": function () {
          var myAPI = { method: function () {} };

          var mock = sinon.mock(myAPI);
          mock.expects("method").once();

          PubSub.subscribe("message", myAPI.method);
          PubSub.publishSync("message", undefined);

          mock.verify();
      }
    

Js-Test-Driver

JS Test Runner

Js-Test-Driver

JS Test Runner

Vows

BDD for Node.js

Vows

      vows.describe('Division by Zero').addBatch({
          'when dividing a number by zero': {
              topic: function () { return 42 / 0 },

              'we get Infinity': function (topic) {
                  assert.equal(topic, Infinity);
              }
          },
          'but when dividing zero by zero': {
              topic: function () { return 0 / 0 },

              'we get a value which': {
                  'is not a number': function (topic) {
                      assert.isNaN(topic);
                  },
                  'is not equal to itself': function (topic) {
                      assert.notEqual(topic, topic);
                  }
              }
          }
      }).run();
    

Nodeunit

BDD for Node.js

Nodeunit

      exports.testSomething = function(test){
          test.expect(1);
          test.ok(true, "this assertion should pass");
          test.done();
      };

      exports.testSomethingElse = function(test){
          test.ok(false, "this assertion should fail");
          test.done();
      };
    

There's more...

but few of quality

Wikipedia's List of Javascript Unit Testing Frameworks









Backbone Model and View Testing

with Jasmine and Sinon.js

Backbone Testing Framework

Why Jasmine Dominates

Backbone Testing Framework

Why Sinon.js Dominates

Backbone Models (and Collections)

Simple Declarations

      window.MenuItem = Backbone.Model.extend({
        defaults  : {
          "active": false
        }
      });

      window.MenuItemCollection = Backbone.Collection.extend({
        model: MenuItem,
        url  : "../api/project"
      });
    

Backbone Models (and Collections)

Jasmine Model Unit Test Spec Setup

      describe("MenuItem", function () {
        var menuItem;
        var menuItemCollection;

        beforeEach(function () {
          menuItem = new MenuItem();
          menuItemCollection = new MenuItemCollection();
        });

        describe("MenuItem model", function () {
          it("has default property 'active' set to false", function () {
            expect(menuItem.get('active')).toEqual(false);
          });
        });

      ...
      });
    

Backbone Models (and Collections)

Jasmine Collection Functional Test Spec

      describe("MenuItem", function () {

        ...

        describe("MenuItemCollection", function () {
          it("has child models", function () {
            menuItemCollection.fetch({async: false});
            expect(menuItemCollection.models.length).toBeGreaterThan(0);
          });
        });

      ...
      });
    

Backbone Models (and Collections)

Jasmine Collection Functional-to-Unit Test Spec with sinon-server.js

      describe("MenuItem", function () {

        ...

        describe("MenuItemCollection", function () {
          it("has child models", function () {
            menuItemCollection.fetch({async: false});
            expect(menuItemCollection.models.length).toBeGreaterThan(0);
          });

          it("has child models from Mock", function () {
            var server = sinon.fakeServer.create();
            server.respondWith([200, {"Content-Type": "application/json"},
              '[{"id":12345678, "title":"Sudo make me a sandwich"}]']);
            menuItemCollection.fetch({async: false});
            server.respond();
            expect(menuItemCollection.models.length).toEqual(1);
            server.restore();
          });
        });
      
      ...
      });
    

jasmine-jquery.js

jQuery matchers and fixture loader for Jasmine

Backbone Views

Setup Views spec with Jasmine and jasmine-jquery.js

      describe("MenuItem", function () {

        ...

        beforeEach(function () {
          menuItem = new MenuItem();
          menuItemCollection = new MenuItemCollection();
        });

        ...

        describe("MenuItemCollection View", function () {
          var view;
          var mockMenuItemCollection;

          beforeEach(function () {
            mockMenuItemCollection = new MenuItemCollection(window.mock.menuItemCollection);
            view = new MenuListView({model: mockMenuItemCollection});
          });

        ...
      });
    

Backbone Views

Testing Views with Jasmine and jasmine-jquery.js

      describe("MenuItem", function () {

        ...

        describe("MenuItemCollection View", function () {

          ...

          it("has none selected by default", function () {
            var find = $(view.render().el).find('.nav-list .active').length;
            expect(find).toEqual(0);
          });

          it("sets menu item to active when clicked", function () {
            mockMenuItemCollection.at(0).set({active: true});
            var find = $(view.render().el).find('.active');
            expect(find).toExist();
          });

        ...
      });
    









Go forth and Unit Test your Javascript!

Test-Driven JavaScript Development is definitive!