Sunday, April 22, 2012

Using Mocha to test a Backbone application on Node.js

@remosu and I are working on a backbone single page application (SPA).
We started using Jasmine to write our specs, but we felt it a bit awkward for asynchronous testing, so we decided to try with Mocha.

Mocha is a feature-rich JavaScript test framework which promises making asynchronous testing simple and fun. Since the goal of this backbone SPA is just learning, we decided to add node to the game: we’d run Mocha on node.

This post will describe how we set up our environment to test our Backbone code using Mocha on node.

First of all we installed node following this useful link.
Once node was up and running, we installed npm following the instructions in this link.
npm is a package manager for node that we used to install all the modules required to run our tests.

This is the code of our tests:
var should = require('should');
var jQuery = require("jquery");
var Backbone = require('backbone');

var BMModel = Backbone.Model.extend({
    idAttribute: "_id"
});

var BMCollection = Backbone.Collection.extend({
    model: BMModel,
    url : "nn"
});

describe("BMCollection", function(){
    var bmcollection;
    beforeEach(function(){
         bmcollection = new BMCollection({url:'collection_tests'});
    });          
    it("is defined", function(){
        bmcollection.should.not.equal(undefined);
    });
});

describe("banana", function(){
    var bmcollection;

    describe("read all", function(){
        beforeEach(function(){
            bmcollection = new BMCollection();
            bmcollection.url = '/collection_tests/';
        });

        it("gets contents from banana", function(){
            bmcollection.fetch();
        });
    });
});
The second describe is not testing anything yet, we just wanted to run it to confirm that our environment was correctly set up. But it did not worked, because it couldn't find some dependencies.
To make it work, we had to install the following node modules: mocha, backbone, jquery and should.
Ok, but where should they be installed?
The nmp FAQ explains what should be installed locally and what should be installed globally:
I installed something globally, but I can't `require()` it
Install it locally.
The global install location is a place for command-line utilities to put their bins in the system PATH. It's not for use with require().
If you require() a module in your code, then that means it's a dependency, and a part of your program. You need to install it locally in your program
So we installed should, jquery and backbone locally using:
$ npm install should
$ npm install jquery
$ npm install backbone
whereas we installed mocha (that was being used as a command) globally:
$ npm install -g mocha
Having done that we tried to run our tests again doing:
$ mocha --reporter spec test/acceptance/*.js
but we got an error saying that an ajax object was undefined in fetch.

That puzzled us for a while. We had to check the backbone source code and think for a while (we are just beginners with JavaScript) to realize how to fix it.
This is a fragment of the backbone source code:
var root = this;
...
var $ = root.jQuery || root.Zepto || root.ender;
We think that the problem was that backbone was not setting its $ variable properly, probably because this was not referencing the global object when called from mocha in node.
After some trial and error, we used the setDomLibrary() method to inject jQuery as the DOM manipulation and Ajax calls library in backbone:
var should = require('should');
var jQuery = require("jquery");
var Backbone = require('backbone');
Backbone.setDomLibrary(jQuery);
This is not necessary when using backbone directly because the $ is initialized by default to jQuery, Zepto or Ender.
In this case it was failing because the this object wasn’t the global object.
Now, finally, the tests run and we were ready to start coding.

No comments:

Post a Comment