I don’t have all the time that I would like for writing here but before the end of the year it’s time for a new blog post.
This year has been really prolific for me learning new things with JavaScript and in the last months I have adopted the Yeoman workflow, which really helps to develop web applications.
One of the tools included in Yeoman is Grunt, a task runner that is used to build, preview and test the project.
In the process of converting a Backbone.js + RequireJS app to this Yeoman workflow, I had a hard time figuring the right configuration to run tests both in the command line and the browser. There are several blog posts and repositories talking about it but non of them worked for me so I’m summarizing my experience here.
I’m assuming you have created an application using Yeoman as follows:
> mkdir app > cd app > yo backbone
So the mocha task in the Gruntfile.js is the default one but the run parameter has to be set up to false. You can change the mocha reporter as you like and add as well options for logging so you can track any error.
mocha: { all: { options: { log: true, reporter: 'Spec', run: false, timeout: 10000, urls: ['http://localhost:<%= connect.test.options.port %>/index.html'] } } },
I added Mocha and Chai as developer dependencies so they can be downloaded with the package manager Bower, which is another Yeoman tool. After this you can remove the lib folder inside of the test folder.
{ "name": "backbone-app", "version": "1.0.0", "dependencies": { "jquery": "~1.9.0", "underscore": "~1.4.3", "backbone": "~1.0.0", "requirejs": "~2.1.5", "requirejs-text": "~2.0.5" }, "devDependencies": { "mocha": "*", "chai": "*" } }
Another key point was including the test framework and the assertion library inside of script tags in the HTML code and not trying to load them using RequireJS.
<!doctype html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Mocha Spec Runner</title> <link rel="stylesheet" href="bower_components/mocha/mocha.css"> </head> <body> <div id="mocha"></div> <script src="bower_components/mocha/mocha.js"></script> <script>mocha.setup('bdd')</script> <script src="bower_components/chai/chai.js"></script> <script>var expect = chai.expect</script> <script data-main="spec/runner" src="bower_components/requirejs/require.js"></script> </body> </html>
Then I created a spec runner which is the main entry point for RequireJS to load the test files and its configuration.
'use strict'; require.config({ baseUrl: 'scripts/', paths: { jquery: '../bower_components/jquery/jquery', backbone: '../bower_components/backbone/backbone', underscore: '../bower_components/underscore/underscore' }, shim: { underscore: { exports: '_' }, jquery: { exports: '$' }, backbone: { deps: ['underscore', 'jquery'], exports: 'Backbone' } } }); var specs = [ 'spec/collections.examples.js', 'spec/models.example.js' ]; require(specs, function() { mocha.run(); });
Now you can create a test file for a Backbone collection like this:
/*global define, describe, it, expect */ 'use strict'; define(function(require) { var ExamplesCollection = require('collections/examples'); describe('Examples collection', function() { var examples = new ExamplesCollection(); it('should exist', function() { expect(examples).to.exist; }); it('should be an instance of Examples collection', function() { expect(examples).to.be.an.instanceof(ExamplesCollection); }); }); });
To run the tests through the command line you just need to run grunt test
. If you want to run them in the browser load http://localhost:9001/index.html after typing grunt server:test
.
Don’t forget as well to run bower install
to download the dependencies and check that the app folder is being mounted for the test task.
test: { options: { port: 9001, middleware: function (connect) { return [ mountFolder(connect, 'test'), mountFolder(connect, yeomanConfig.app) ]; } } },
Merry Christmas and Happy Coding!
Thank you. I got my tests running using the structure you proposed.
You’re welcome. It took me a while to figure it out so I’m glad it helped somebody else. Cheers.