Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Browser Unit Testing with Backbone Mocha Chai and RequireJS
<html>
<head>
<meta charset="utf-8"/>
<title>Backbone Tests</title>
<link rel="stylesheet" href="libs/mocha.css"/>
</head>
<body>
<div id="mocha"></div>
<script data-main="SpecRunner.js" src="/app/libs/require.js"></script>
</body>
</html>
define(function(require) {
var models = require('models');
describe('Models', function() {
describe('Sample Model', function() {
it('should default "urlRoot" property to "/api/samples"', function() {
var sample = new models.Sample();
sample.urlRoot.should.equal('/api/samples');
});
});
});
});
define(function(require) {
var Backbone = require('backbone');
var models = {};
models.Sample = Backbone.Model.extend({
urlRoot: '/api/samples'
});
return models;
});
require.config({
baseUrl: '/backbone-tests/',
paths: {
'jquery' : '/app/libs/jquery',
'underscore' : '/app/libs/underscore',
'backbone' : '/app/libs/backbone',
'mocha' : 'libs/mocha',
'chai' : 'libs/chai',
'chai-jquery' : 'libs/chai-jquery',
'models' : '/app/models'
},
shim: {
'chai-jquery': ['jquery', 'chai']
},
urlArgs: 'bust=' + (new Date()).getTime()
});
define(function(require) {
var chai = require('chai');
var mocha = require('mocha');
require('jquery');
require('chai-jquery');
// Chai
var should = chai.should();
chai.use(chaiJquery);
mocha.setup('bdd');
require([
'specs/model-tests.js',
], function(require) {
mocha.run();
});
});
@craigrich

This comment has been minimized.

Show comment Hide comment
@craigrich

craigrich Mar 24, 2013

Thanks for this! are there any differences for using 'expect' over 'should' in chai? or is it down to personal preference?

Thanks for this! are there any differences for using 'expect' over 'should' in chai? or is it down to personal preference?

@thaddeusalbers

This comment has been minimized.

Show comment Hide comment
@AlexZeitler

This comment has been minimized.

Show comment Hide comment
@AlexZeitler

AlexZeitler Mar 27, 2013

Thanks for sharing. I'm using a similar structure which works fine inside the browser but it fails when using mocha-phantomjs (http://stackoverflow.com/questions/15657458/mocha-init-timeout-with-mocha-phantomjs). Did you ever test your solution with mocha-phantomjs? (https://github.com/metaskills/mocha-phantomjs)

Thanks for sharing. I'm using a similar structure which works fine inside the browser but it fails when using mocha-phantomjs (http://stackoverflow.com/questions/15657458/mocha-init-timeout-with-mocha-phantomjs). Did you ever test your solution with mocha-phantomjs? (https://github.com/metaskills/mocha-phantomjs)

@phillycoder

This comment has been minimized.

Show comment Hide comment
@phillycoder

phillycoder Apr 1, 2013

Thanks for this gist, very useful.

I had to add a line after setup, to let mocha continue run tests after first fail.

  mocha.setup('bdd');
  mocha.bail(false);

Thanks for this gist, very useful.

I had to add a line after setup, to let mocha continue run tests after first fail.

  mocha.setup('bdd');
  mocha.bail(false);
@kumarharsh

This comment has been minimized.

Show comment Hide comment
@kumarharsh

kumarharsh May 11, 2013

@craigrich : Check out this page for the differences between expect & should. http://chaijs.com/guide/styles/

@craigrich : Check out this page for the differences between expect & should. http://chaijs.com/guide/styles/

@kumarharsh

This comment has been minimized.

Show comment Hide comment
@kumarharsh

kumarharsh May 11, 2013

+1 for this gist. Awesome!

+1 for this gist. Awesome!

@rwestgeest

This comment has been minimized.

Show comment Hide comment
@rwestgeest

rwestgeest Jul 9, 2013

Thanks for sharing. I have searched for hours for such a clear example. Found lots. This one really helped.

@AlexZeitler To run with mocha-phantomjs:

change:

    mocha.run();

to

    if (window.mochaPhantomJS) {
      mochaPhantomJS.run();
    }
    else {
      mocha.run();
    }

in SpecRunner.js

to use with expect i had to add th following line to SpecRunner.js

expect = chai.expect;  // note no 'var'

near

var should = chai.should();

(actually, I replaced that line.

Thanks for sharing. I have searched for hours for such a clear example. Found lots. This one really helped.

@AlexZeitler To run with mocha-phantomjs:

change:

    mocha.run();

to

    if (window.mochaPhantomJS) {
      mochaPhantomJS.run();
    }
    else {
      mocha.run();
    }

in SpecRunner.js

to use with expect i had to add th following line to SpecRunner.js

expect = chai.expect;  // note no 'var'

near

var should = chai.should();

(actually, I replaced that line.

@mnoble01

This comment has been minimized.

Show comment Hide comment
@mnoble01

mnoble01 Aug 6, 2013

@rwestgeest, thanks so much! I was searching for a solution to @AlexZeitler's same problem for a couple hours.

mnoble01 commented Aug 6, 2013

@rwestgeest, thanks so much! I was searching for a solution to @AlexZeitler's same problem for a couple hours.

@kucherenko

This comment has been minimized.

Show comment Hide comment
@kucherenko

kucherenko Aug 21, 2013

Thank you, very useful gist.
Is it possible to run this tests with mocha command in terminal without phantomjs?

Thank you, very useful gist.
Is it possible to run this tests with mocha command in terminal without phantomjs?

@htulipe

This comment has been minimized.

Show comment Hide comment
@htulipe

htulipe Sep 8, 2013

Very useful, thanks a lot

htulipe commented Sep 8, 2013

Very useful, thanks a lot

@chrishokamp

This comment has been minimized.

Show comment Hide comment
@chrishokamp

chrishokamp Oct 21, 2013

Thanks for this great gist!

I also had to add shims for mocha and chai to get this to work via grunt test:

shim: {
    ...,
    mocha: {
         exports: 'mocha'
    },
    chai: {
         exports: 'chai'
    }
},

Thanks for this great gist!

I also had to add shims for mocha and chai to get this to work via grunt test:

shim: {
    ...,
    mocha: {
         exports: 'mocha'
    },
    chai: {
         exports: 'chai'
    }
},
@gumaflux

This comment has been minimized.

Show comment Hide comment
@gumaflux

gumaflux Dec 11, 2013

Thanks @rwestgeest got tripped by same issue head banging for a little while there..

Thanks @rwestgeest got tripped by same issue head banging for a little while there..

@dminkovsky

This comment has been minimized.

Show comment Hide comment
@dminkovsky

dminkovsky Feb 28, 2014

Coolness, thank you.

Coolness, thank you.

@The1nternet

This comment has been minimized.

Show comment Hide comment
@The1nternet

The1nternet Mar 14, 2014

Would someone be kind enough to explain how var should = chai.should() works when using RequireJS like in this example? Using "var" will scope "should" to within that single require function, therefore making it not available in the test fixtures. When I follow the example, it does not make "should" available in my test fixtures. Instead, i have to use the alias Should(), and I have to assign it to the window object like this: window.Should = chai.Should()
How are all of you able to get "should" to work in your tests without doing what i just described?
When i do this then, in my tests, i have to use Should, instead of should...
Here's a little explanation of why you have to do this, if you're making "should" global: chaijs/chai#86
and if you're NOT making it global, then how is it available in your tests?

Would someone be kind enough to explain how var should = chai.should() works when using RequireJS like in this example? Using "var" will scope "should" to within that single require function, therefore making it not available in the test fixtures. When I follow the example, it does not make "should" available in my test fixtures. Instead, i have to use the alias Should(), and I have to assign it to the window object like this: window.Should = chai.Should()
How are all of you able to get "should" to work in your tests without doing what i just described?
When i do this then, in my tests, i have to use Should, instead of should...
Here's a little explanation of why you have to do this, if you're making "should" global: chaijs/chai#86
and if you're NOT making it global, then how is it available in your tests?

@steeren

This comment has been minimized.

Show comment Hide comment
@steeren

steeren Aug 20, 2014

@michaelcox, great work, thank you - very useful.
Just one correction. The filename model-test.js doesn't match the actual filename (model-tests.js).

In specRunner.js it should read:
require([
'specs/model-tests.js',
],

Also, one tiny tiny little thing (not really a bug but more for your information), your require call, requires require :-) Clearly if require is already in scope (its global) then your require call, really shouldn't be requiring itself.
This would be more correct:
require(['chai', 'chai-jquery', 'mocha'], function(chai, chaiJquery, mocha){

// Init Chai
chai.should(); //initializes chai.should()
chai.use(chaiJquery);

/*globals mocha */
mocha.setup('bdd');

require([
'specs/model-test.js',
], function(require) {
mocha.run();
});

});
note:
we require mocha in the block above, but what is passed in is wrong. This can be corrected by adding this to the shim section:
shim: {
'mocha': { exports: 'mocha'},

And finally....
@the1nernet,
The line:
// Chai
var should = chai.should();

Is indeed misleading and I can't explain the rational behind it, but I can tell you that the magic occurs because the first time you call chai.should() the should() function is defined and initialised (it always helps to read/debug through these libraries - treating any JS library like a blackbox is a mistake - in almost all cases.
For me things worked after I changed the filename bug, and I simply changed the chai.should() line above to be a tiny bit more helpful to me:
// Initialize the Chai.should() function
chai.should();

Still though, it is weird and understandable that it caused confusion.

steeren commented Aug 20, 2014

@michaelcox, great work, thank you - very useful.
Just one correction. The filename model-test.js doesn't match the actual filename (model-tests.js).

In specRunner.js it should read:
require([
'specs/model-tests.js',
],

Also, one tiny tiny little thing (not really a bug but more for your information), your require call, requires require :-) Clearly if require is already in scope (its global) then your require call, really shouldn't be requiring itself.
This would be more correct:
require(['chai', 'chai-jquery', 'mocha'], function(chai, chaiJquery, mocha){

// Init Chai
chai.should(); //initializes chai.should()
chai.use(chaiJquery);

/*globals mocha */
mocha.setup('bdd');

require([
'specs/model-test.js',
], function(require) {
mocha.run();
});

});
note:
we require mocha in the block above, but what is passed in is wrong. This can be corrected by adding this to the shim section:
shim: {
'mocha': { exports: 'mocha'},

And finally....
@the1nernet,
The line:
// Chai
var should = chai.should();

Is indeed misleading and I can't explain the rational behind it, but I can tell you that the magic occurs because the first time you call chai.should() the should() function is defined and initialised (it always helps to read/debug through these libraries - treating any JS library like a blackbox is a mistake - in almost all cases.
For me things worked after I changed the filename bug, and I simply changed the chai.should() line above to be a tiny bit more helpful to me:
// Initialize the Chai.should() function
chai.should();

Still though, it is weird and understandable that it caused confusion.

@michaelcox

This comment has been minimized.

Show comment Hide comment
@michaelcox

michaelcox Aug 23, 2014

Thanks @steeren - it's been a while since I wrote this, but I fixed the two things you pointed out. Yes I wouldn't need to require "require", and the link to model-tests.js was incorrect.

For the "mocha" require, however, I think it should work without a shim. The latest version of mocha at least has support for requirejs.

Owner

michaelcox commented Aug 23, 2014

Thanks @steeren - it's been a while since I wrote this, but I fixed the two things you pointed out. Yes I wouldn't need to require "require", and the link to model-tests.js was incorrect.

For the "mocha" require, however, I think it should work without a shim. The latest version of mocha at least has support for requirejs.

@michaelcox

This comment has been minimized.

Show comment Hide comment
@michaelcox

michaelcox Aug 24, 2014

A link to the original blog post as well as some additional thoughts on unit testing in backbone can be found here:

http://mdcox.net/posts/backbone-unit-testing.html

Owner

michaelcox commented Aug 24, 2014

A link to the original blog post as well as some additional thoughts on unit testing in backbone can be found here:

http://mdcox.net/posts/backbone-unit-testing.html

@indigofeather

This comment has been minimized.

Show comment Hide comment
@indigofeather

indigofeather Jan 3, 2015

Very useful, thanks a lot

Very useful, thanks a lot

@danascheider

This comment has been minimized.

Show comment Hide comment
@danascheider

danascheider Jan 13, 2015

I've been looking for something like this for weeks, thank you so much!

I've been looking for something like this for weeks, thank you so much!

@sfahlberg

This comment has been minimized.

Show comment Hide comment
@sfahlberg

sfahlberg Feb 5, 2015

I'm having difficulty loading mocha (it says: "Uncaught TypeError: Cannot read property 'setup' of undefined"). The reason is because the variable "mocha" is undefined. I downloaded mocha.js from https://github.com/mochajs/mocha/blob/master/mocha.js but for some reason that doesn't seem to be the right code. I looked online and everyone says to use npm and nobody mentions copying. Can someone clarify?

I'm having difficulty loading mocha (it says: "Uncaught TypeError: Cannot read property 'setup' of undefined"). The reason is because the variable "mocha" is undefined. I downloaded mocha.js from https://github.com/mochajs/mocha/blob/master/mocha.js but for some reason that doesn't seem to be the right code. I looked online and everyone says to use npm and nobody mentions copying. Can someone clarify?

@danascheider

This comment has been minimized.

Show comment Hide comment
@danascheider

danascheider Feb 12, 2015

sfahlberg - take a look and see if my gist helps you. I changed a few things from this one because I had a some problems on account of the fact that I didn't want my index.html to consist of the test code, but I remember I also had problems like the one you're describing and they are gone now.

https://gist.github.com/danascheider/82eda70a4f7152841dca

Let me know if this helps.

sfahlberg - take a look and see if my gist helps you. I changed a few things from this one because I had a some problems on account of the fact that I didn't want my index.html to consist of the test code, but I remember I also had problems like the one you're describing and they are gone now.

https://gist.github.com/danascheider/82eda70a4f7152841dca

Let me know if this helps.

@danascheider

This comment has been minimized.

Show comment Hide comment
@danascheider

danascheider Feb 12, 2015

And michaelcox, I just wanted to thank you again for this gist, I have been testing successfully for weeks now and honestly don't know if I would have gotten to this point without it. You added some much-needed clarity to a hair-pulling process.

And michaelcox, I just wanted to thank you again for this gist, I have been testing successfully for weeks now and honestly don't know if I would have gotten to this point without it. You added some much-needed clarity to a hair-pulling process.

@Tallowman

This comment has been minimized.

Show comment Hide comment
@Tallowman

Tallowman Mar 11, 2015

@sfahlberg - I have had the same problem and have fixed it by simply not assigning require('mocha') to var mocha ie, this line:
var mocha = require('mocha');
becomes
require('mocha');

I am very new to TDD and require, and I'm not really sure why this works. I also copied the code, but from here, the link I found on another blog post ( it looks like the same code ):
https://github.com/visionmedia/mocha/raw/master/mocha.js

If anyone can tell me why I've had to apply this fix, or if you have found a solution sfalhberg, to making it work with the copied code I would be most grateful :)

@sfahlberg - I have had the same problem and have fixed it by simply not assigning require('mocha') to var mocha ie, this line:
var mocha = require('mocha');
becomes
require('mocha');

I am very new to TDD and require, and I'm not really sure why this works. I also copied the code, but from here, the link I found on another blog post ( it looks like the same code ):
https://github.com/visionmedia/mocha/raw/master/mocha.js

If anyone can tell me why I've had to apply this fix, or if you have found a solution sfalhberg, to making it work with the copied code I would be most grateful :)

@mike-kelly

This comment has been minimized.

Show comment Hide comment
@mike-kelly

mike-kelly Mar 20, 2015

@sfahlberg - I had the same issue. This is how I got it to work:

requirejs.config({
    baseUrl: '../client/js/',
    paths: {
        'jquery'        : 'libs/jquery',
        'underscore'    : 'libs/underscore',
        'backbone'      : 'libs/backbone',
        'mocha'         : '../../tests/libs/mocha',
        'chai'          : '../../tests/libs/chai',
        'chai-jquery'   : '../../tests/libs/chai-jquery',
        'cardmodels'    : 'app/models/cardmodels'
    },
    shim: {
        'chai-jquery': ['jquery', 'chai'],
        'mocha': {
            init: function () {
                this.mocha.setup('bdd');
                return this.mocha;
            }
        },
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        }
    },
    urlArgs: 'bust=' + (new Date()).getTime()
});

define([
    'chai',
    'chai-jquery',
    'mocha'
],

function (chai, chaiJquery, mocha) {

    // Chai
    var should = chai.should();
    chai.use(chaiJquery);

    require([
        'specs/cardmodels-tests.js',
    ], function(require) {
        mocha.run();
    });
});

See in particular the shims section. The mocha shim which does mocha.setup was what got mocha working for me.

I also had to shim Backbone to get it working with the actual tests.

Also note that my paths are different, as I've set this up to run from a tests folder in my working app. The index.html file is inside tests

@michaelcox - in your gist chaiJquery is undefined since the recent file revision

@sfahlberg - I had the same issue. This is how I got it to work:

requirejs.config({
    baseUrl: '../client/js/',
    paths: {
        'jquery'        : 'libs/jquery',
        'underscore'    : 'libs/underscore',
        'backbone'      : 'libs/backbone',
        'mocha'         : '../../tests/libs/mocha',
        'chai'          : '../../tests/libs/chai',
        'chai-jquery'   : '../../tests/libs/chai-jquery',
        'cardmodels'    : 'app/models/cardmodels'
    },
    shim: {
        'chai-jquery': ['jquery', 'chai'],
        'mocha': {
            init: function () {
                this.mocha.setup('bdd');
                return this.mocha;
            }
        },
        'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        }
    },
    urlArgs: 'bust=' + (new Date()).getTime()
});

define([
    'chai',
    'chai-jquery',
    'mocha'
],

function (chai, chaiJquery, mocha) {

    // Chai
    var should = chai.should();
    chai.use(chaiJquery);

    require([
        'specs/cardmodels-tests.js',
    ], function(require) {
        mocha.run();
    });
});

See in particular the shims section. The mocha shim which does mocha.setup was what got mocha working for me.

I also had to shim Backbone to get it working with the actual tests.

Also note that my paths are different, as I've set this up to run from a tests folder in my working app. The index.html file is inside tests

@michaelcox - in your gist chaiJquery is undefined since the recent file revision

@mikealexander

This comment has been minimized.

Show comment Hide comment
@mikealexander

mikealexander Jun 8, 2015

Thank-you @michaelcox and @mike-kelly. Got my tests up and running after a frustrating couple of hours.

Thank-you @michaelcox and @mike-kelly. Got my tests up and running after a frustrating couple of hours.

@leftclickben

This comment has been minimized.

Show comment Hide comment
@leftclickben

leftclickben Nov 27, 2015

Thanks for this, very useful. In my scenario, I wanted the tests to run both under gulp-mocha-requirejs and in the browser. This is what the core of my equivalent to SpecRunner.js looks like:

require.config({
    paths: {
        mocha: '../node_modules/mocha/mocha',
        chai: '../node_modules/chai/chai',
        // ... my modules ...
    },
    shim: {
        mocha: {
            init: function () {
                return this.mocha.setup({
                    ui: 'bdd',
                    reporter: /phantom/i.test(window.navigator.userAgent) ? 'spec' : 'html'
                });
            }
        }
    }
});

require(
    [
        'mocha'
    ],
    function (mocha) {
        require(
            [
                './spec/unit/main.spec'
            ],
            function () {
                mocha.run();
            }
        );
    }
);

The userAgent switch feels a bit hacky but it works.

Thanks for this, very useful. In my scenario, I wanted the tests to run both under gulp-mocha-requirejs and in the browser. This is what the core of my equivalent to SpecRunner.js looks like:

require.config({
    paths: {
        mocha: '../node_modules/mocha/mocha',
        chai: '../node_modules/chai/chai',
        // ... my modules ...
    },
    shim: {
        mocha: {
            init: function () {
                return this.mocha.setup({
                    ui: 'bdd',
                    reporter: /phantom/i.test(window.navigator.userAgent) ? 'spec' : 'html'
                });
            }
        }
    }
});

require(
    [
        'mocha'
    ],
    function (mocha) {
        require(
            [
                './spec/unit/main.spec'
            ],
            function () {
                mocha.run();
            }
        );
    }
);

The userAgent switch feels a bit hacky but it works.

@amitrai99

This comment has been minimized.

Show comment Hide comment
@amitrai99

amitrai99 Jan 18, 2016

Great gist, thanks for this.
Is there a way to run a single test using this method?

Great gist, thanks for this.
Is there a way to run a single test using this method?

@maryjanebarger

This comment has been minimized.

Show comment Hide comment
@maryjanebarger

maryjanebarger Jun 30, 2016

Can someone post their Gruntfile.js or gulpfile.js used to run this code? Thanks.

Can someone post their Gruntfile.js or gulpfile.js used to run this code? Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment