In the following post I would like to introduce one way how you can setup your testing workflow for JavaScript development. The central components in the testing environment are Grunt, Mocha and Chai that I will cover from the introduction and installation of each component to the cooperation of all components for the execution of tests.
If you are already an experienced Grunt user and just look for the Gruntfile.js and the Mocha / Chai setup just skip the central components section and skip to the installing components part.
You can find the sample project with all code at GitHub on: https://github.com/maicki/sample-js-testing-grunt-mocha-chai
As mentioned in the introduction to this post, the central components of this post are Grunt, Mocha and Chai. In the following section I would like to explain these components a bit more in details, so you know what task each components is used for.
Built on top of Node.js, Grunt is a task-based command-line tool that speeds up workflows by reducing the effort required to prepare assets for production. It does this by wrapping up jobs into tasks that are compiled automatically as you go along. Basically, you can use Grunt on most tasks that you consider to be Grunt work and would normally have to manually configure and run yourself. What kind of tasks? Well, the list is exhaustive. Suffice it to say, Grunt can handle most things you want to have within your JavaScript workflow, from minifying to concatenating JavaScript as well as running Mocha tests automatically what is pretty handy for our use case.
Mocha is a feature-rich JavaScript test framework running on Node.js and the browser, making asynchronous testing simple and fun. It does just about everything a JavaScript developer needs, and yet remains customisable enough to support both behaviour-driven and test-driven development styles. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases.
Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.
Chai aims to be an expressive and easily approachable way to write assertions for JavaScript project testing. Developers have already started expanding on Chai’s available language through plugins such as spies, mocks/stubs, and jQuery support.
Interesting is that the motivation for Chai came about with the release of Mocha. At the time, there was no apparent assertion library to pair with it that would allow for the same assertions to be used on both server and client with the inherent simplicity that Mocha provides.
It's not necessary to use Chai as assertion library instead of Node.js assert module, if you only want to run your tests within the terminal. As I need to run Mocha tests within the browser, to test things like the HTML5 Filesystem API, I need an extra assert library and so I decided to use Chai for that.
To prepare our sample project just create a folder with the name: "sample-testing-project". Within this project folder we will execute all further commands if no other instructions are given.
All three components need Node.js installed on your machine. The installation of node should not be the topic of this post. You can find a detailed Node.js installation instruction over at Node.js wiki on GitHub: https://github.com/joyent/node/wiki/Installation
Once Node.js is installed, run the following command to install Grunt globally:
$ npm install -g grunt-cli
To make sure Grunt has been properly installed, you can run the following command:
$ grunt --version
The output should look like the following (the version number can differ):
grunt-cli v0.1.11
In the next step we have to create a package.json file within the project directory. The JSON file enables us to track and install all of our development dependencies. This is useful when someone else clones our repository; instead of tracing down our dependencies manually, they can simply run "npm install" and NPM will download the correct version of all dependencies fro them so anyone who works on the project will have the most current dependencies, which ultimately helps to keep the development environments in sync. The package.json should contain:
{
"name": "sample-testing-project",
"version": "0.0.1",
"devDependencies": {
"grunt": "latest"
}
}
After creating the package.json file you have to execute the following command to install the currently listed dependencies for your project:
$ npm install
All dependency files will be placed within a node_modules within your project folder.
With the knowledge, how to install npm modules, it's easy to install Mocha and Chai with the following commands:
$ npm install mocha --save-dev
$ npm install chai --save-dev
Using the --save-dev flag will cause npm to automatically add this package to the dev dependencies in your package.json file. This is a handy little trick to save you some time and make sure that you don’t forget to update the file yourself.
As we learned from the introductory part, Grunt is basically only a task runner to run tasks like minifying JavaScript or like in our case, running tests. For most of the tasks, Grunt plugins already exist. Let's search on npm for plugins we could use to run Mocha tests:
$ npm search gruntplugin mocha
The shortened version of the output will look like the following:
...
grunt-jscover Grunt task for jscover
grunt-jscoverage Grunt task for jscoverage; which will parse your source code and generate an instrumented
grunt-mocha Grunt task for running client-side Mocha specs in PhantomJS
grunt-mocha-appium Run functional Mocha tests with wd and Appium.
grunt-mocha-browser Grunt task for run mocha test suite in browser
....
It seems like we already found a good candidate for that: grunt-mocha. Let's install that via npm and add it to the dev dependencies.
npm install grunt-mocha --save-dev
After installing all necessaries components, your packaged.json should look like the following (version number can differ):
{
"name": "sample-testing-project",
"version": "0.0.1",
"devDependencies": {
"grunt": "latest",
"grunt-mocha": "~0.4.6",
"mocha": "~1.14.0",
"chai": "~1.8.1"
}
}
Now it is time to create the Gruntfile. A Gruntfile is a JavaScript file that Grunt leverages to understand your projects tasks and configuration. It is essentially made up of a wrapper function that takes Grunt as an argument. When you run "grunt" from the command line, Grunt will recurse upwards till it finds your Gruntfile. This functionality allows you to run Grunt from any sub directory of your project.
The basic Gruntfile.js looks like the following:
// Gruntfile.js
module.exports = function(grunt){
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});
grunt.registerTask('default', ['mocha']);
};
You are now set up to run Grunt from the command line at the root of your project. But if you do so at this stage, you will get the following warning:
$ grunt
Warning: Task "mocha" not found. Use --force to continue.
Aborted due to warnings.
We’d get this because we haven’t specified any tasks or dependencies yet other than Grunt. Let's change this and add the Mocha task:
// Gruntfile.js
module.exports = function(grunt){
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Mocha
mocha: {
all: {
src: ['tests/testrunner.html'],
},
options: {
run: true
}
}
});
// Load grunt mocha task
grunt.loadNpmTasks('grunt-mocha');
grunt.registerTask('default', ['mocha']);
};
Within the updated Gruntfile.js we have now added the grunt-mocha task as well as the information for executing the Grunt Mocha task within Grunt. As you can see from within the file the next step is to create a folder that is called "tests". Within the tests folder create a file called testrunner.html with the following content:
<html>
<head>
<meta charset="utf-8">
<title>Mocha Tests</title>
<link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="../node_modules/mocha/mocha.js"></script>
<script src="../node_modules/chai/chai.js"></script>
<script>
mocha.setup('bdd')
mocha.reporter('html');
</script>
<!-- Tests -->
<script></script>
<script>
// Only tests run in real browser, injected script run if options.run == true
if (navigator.userAgent.indexOf('PhantomJS') < 0) {
mocha.run();
}
</script>
</body>
</html>
Now, everytime you type the command "grunt" within the terminal, Grunt will run Mocha and Mocha will run the testrunner.html.
Finally to test our setup, we will just add a sample tests. Create a new file within your test folder called tests.js and add the following test to the file:
// tests.js
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
chai.assert.equal(-1, [1,2,3].indexOf(5));
chai.assert.equal(-1, [1,2,3].indexOf(0));
});
});
});
Next step is to add the tests.js file to our testrunner.html. The new testrunner.html part should look like the following:
...
<script>
mocha.setup('bdd')
mocha.reporter('html');
</script>
<!-- Tests -->
<script src="tests.js"></script>
...
If you run the "grunt" command in the terminal you should see the following output and you be ready to add further tests to the tests.js or split tests up in more files and add this to the testrunner.html:
$ grunt
Running "mocha:all" (mocha) task
Testing: tests/testrunner.html
․
1 passing (107ms)
>> 1 passed! (0.11s)
Done, without errors.
Furthermore you can see the Mocha test results within the browser, if you open the tests/testrunner.html file within your browser of choice.