my github
english | español
Waldo Urribarri HOME PROJECTS ABOUT ME


Testing RequireJS-Backbone.js-JQuery-Underscore applications using Jasmine

jasmine

Testing software is important, the sooner the better. Whether you are working on a small or big application, you may introduce bugs unintentionally while doing modifications, and having tests (unit or functional) will help catch those even at build time, or at the very least before going into QA phase. This time I will show my approach to implement tests on my ES5 web application that is structured using RequireJS, Backbone.js, JQuery, and Underscore, using one neat tool called Jasmine.

I won't cover how to create tests as there is plenty of documentation for that, but only how to setup everything to start using them. I had an old project which didn't have any testing implemented and I wanted to try to add those.

My project is structured this way:

/
|__ js/
|______ models/
|______ views/
|______ [other .js files]
|__ node_modules/
|__ test/
|______ main.js
|______ SpecRunner.html
|__ index.html
|__ [everthing else]

So, main thing is adding jasmine package into my package.json. Just cd into the root folder of the project and:

npm install –save-dev jasmine

Hint: If you are running into “Uncaught TypeError: Cannot read property 'spies' of undefined thrown” error, you could revert to 2.4.1 version. Use npm install –save-dev jasmine@2.4.1 instead.

Once you have the package, it's time to configure our SpecRunner. Personally, I prefer to include it into a /test folder. There are several templates to create it, but I'll show the one I used:

<!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8" />
      <title>Jasmine Spec Runner</title>
      <link rel="shortcut icon" type="image/png" href="../node_modules/jasmine-core/images/jasmine_favicon.png" />
      <link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css" />
      <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js">
      </script>
      <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js">
      </script>
      <script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js">
      </script>
      <script type="text/javascript" src="../node_modules/requirejs/require.js" data-main="main">
      </script>
      <script>
            function includeBody() {
                xhttp = new XMLHttpRequest();
                xhttp.onreadystatechange = function() {
                    if (this.readyState == 4) {
                        if (this.status == 200 || this.status == 0) {
                            var idx1 = this.responseText.indexOf('<body');
                            var idx2 = this.responseText.indexOf('</body>');
                            var body = this.responseText.substring(idx1, idx2 + 7);
                            var container = document.createElement("div");
                            container.style.display = 'none';
                            container.innerHTML = body;
                            document.getElementsByTagName('body')[0].appendChild(container);
                        }
                    }
                } 
                xhttp.open("GET", '../index.html', true);
                xhttp.send();
            }
        </script>
  </head>
  <body>
    <script>includeBody();</script>
  </body>
</html>

Link to complete source: SpecRunner.html

Note: The includeBody() function is something custom for my project. More on that in a bit.

First part of the SpecRunner just loads the core jasmine, and then I load require and my main configuration.

require.config({
  baseUrl: '../', // to set the default folder
  paths: {
    'jasmine': ['./node_modules/jasmine-core/lib/jasmine-core/jasmine'],
    'jasmine-html': ['./node_modules/jasmine-core/lib/jasmine-core/jasmine-html'],
    'jasmine-boot': ['./node_modules/jasmine-core/lib/jasmine-core/boot'],

    //add all modules that are required inside your models/views/etc
  },
  shim: {
    'jasmine-html': {
      deps: ['jasmine']
    },
    'jasmine-boot': {
      deps: ['jasmine', 'jasmine-html']
    }
  }
});

require(['jasmine-boot'], function () {
  require([
    'js/models/EntryAreaModel.spec'
    // require your specs here
    ], function(){
      window.onload();
  })
});

As you can see, in the 'paths' section, you should add the jasmine modules and also all other modules that could be eventually required by yours. Then in the bottom require, add all the specs to be tested.

Link to complete source: main.js

After this, your Backbone modules will be available to be required in your tests. Example:

define(['js/models/EntryAreaModel'], function(EntryAreaModel) {
  describe('EntryAreaModel tests', function() {
    describe('new models', function() {
      var model = new EntryAreaModel();
      it('should have indentType property', function() {
        expect(model.get('indentType')).toBeDefined();
      });
      it('should have indents property', function() {
        expect(model.get('indents')).toBeDefined();
      });
      it('without params are valid', function() {
        expect(model.isValid()).toBe(true);
      });
    });
  });
});

Link to complete source: EntryAreaModel.spec.js

Now, you can open your SpecRunner.html in a browser to run the tests and see any errors.

Jasmine Spec Runner

About the includeBody() function

As as the Backbone Views need a DOM to manipulate and also the Underscore templates to create elements, I needed to add all the body of my index.html to actually get my Views tests working. The best way I figured was to include the body into my SpecRunner. :) I'm not sure if this is the correct way, but it works.

Hope this will help you get your tests going ;)


www.000webhost.com