Testing jQuery code with Mocha, Chai and jsdom

Testing jQuery code (in particular jQuery plugins) can be a lot of fun. Below you will find a few tips on setting up the testing environment on MacOS X and running tests with Mocha, Chai and jsdom.

Mocha vs other test frameworks

Why did I choose Mocha? Jasmine also looks very nice and many people are happy with it, but its development seems to be a little slow lately. Mocha has good documentation and all the features I could ask for, so I decided to give it a try instead. Also let’s not forget Mocha has a nyan cat reporter:

Getting ready

Ideally the jQuery plugin we are going to test should be object-oriented. There are many ready, well-designed boilerplates that help to structure a plugin in an object-oriented way. I found this boilerplate quite useful. But I also wanted it to accept a method and/or options when calling the plugin in this way:

$("element").myplugin();
 
or
 
$("element").myplugin({ option1: true, option2: true });
 
or
 
$("element").myplugin("some_other_method");
 
or
 
$("element").myplugin("some_other_method", { option1: true, option2: true });

Here is a yet another take on a jQuery plugin boilerplate (with inline comments):

https://gist.github.com/3871611

UPDATE, October 2013: Starting a new jQuery plugin? Check out jQuery Boilerplate.

Setting up Mocha

While setting up Mocha I first learned about package.json. This is where we can define all dependencies for our jQuery project. It looks something like this:

{
  "name": "jquery-bar-rating",
  "description": "Minimal, light-weight jQuery ratings."
  "version": "0.1.0",
  "main": "./jquery.barrating",
  "devDependencies": {
    "mocha": "*",
    "chai": "*",
    "jsdom": "*",
    "jquery": "*"
  }
}

With a package.json file in place installing Mocha and all its dependencies is as simple as running “npm install” in the project directory. I assume npm is installed on the system. If not, installing npm is explained on the nodejitsu blog.

Bootstrapping DOM

Usually jQuery acts on DOM elements. But what if we want to execute tests outside of the browser environment, on the command line? jsdom (JavaScript implementation of the W3C DOM) will fill that gap. With jsdom you will be able to bootstrap a DOM by loading a document from an URL with jsdom.env() method or creating it from scratch:

var jsdom = require("jsdom").jsdom;
var doc = jsdom('');
var window = doc.createWindow();

Writing tests

Mocha works with a few different assertion libraries. I picked Chai.js, because I liked its BDD assertion styles. You can choose between “expect” or “should” interfaces for writing tests or mix both. “Expect” interface is recommended over “should”, because it is fully compatible with Internet Explorer once you would decide to run tests in the browser.

describe('bar rating plugin on show', function () {
  beforeEach(function () {
    $('#rating').barrating('show');
  });
 
  it('should be initialized', function () {
    expect($('#rating').data('barrating')).to.be.a('object');
  });
});

Tests should live in a spec file inside the “test” directory. See an example of a spec file with a few Mocha tests.

Running tests

./node_modules/mocha/bin/mocha

Don’t forget to add “node_modules” to .gitignore if you are using git.

More information

Visit Mocha and Chai documentation for more details on writing tests.

5 Responses to Testing jQuery code with Mocha, Chai and jsdom

  • December 4, 2012 at 10:14 pm
    Nils N. Haukås says:

    Great post!

    When testing client-side dom-manipulation should one use jquery click events for pressing buttons or instead run the functions bound to those buttons directly?

    Cheers,
    Nils

  • December 6, 2012 at 12:08 am
    netboy says:

    Good question.

    Most of the times I trigger events, which comes natural with jQuery plugins and is quite convenient.

  • April 11, 2013 at 6:57 pm
    Owzzz says:

    You mention you can run functions like so:

    $("element").myplugin("some_other_method");
    $("element").myplugin("some_other_method", { option1: true, option2: true });

    But how would you pass any expected params to those functions.. the object in the second call just overwrites the defaults/options object

    You could add another conditional:

    // Method and array of values for passing
    if (plugin.hasOwnProperty(method) && options.length !== undefined) {
      plugin.init(options, this);
      return plugin[method].apply(plugin, options);
    }

  • April 15, 2013 at 7:52 am
    netboy says:

    hi Owzzz,

    $("element").myplugin("some_other_method");
    $("element").myplugin("some_other_method", { option1: true, option2: true });

    This was only to demonstrate different ways of calling the plugin. I updated the post to make it more clear.

  • December 13, 2013 at 10:49 am
    Carl-Erik Kopseng says:

    This wasn’t quite as straight forward as I had hoped … I would have liked to have a command line runner that could run the same tests as my browser, much like mocha-phantomjs does, only using jsdom. Any example code?

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>