Getting Started with Mocha and Chai
Posted on
In this post I want to detail the process for getting started with the mocha test framework and the chai assertion library. The code that accompanies this post can be found on github.
Assuming that you're starting from a clean slate, we'll start from the ground up with configuration and move into basic usage.
From an empty repo location, perhaps with just a README and a license file, initialize your package.json
file from the command line using npm init
. If you're not familiar with npm, you can scan the docs to get to know npm better. After initializing and then adding "private": true
, my package.json
looks like this:
{
"name": "sandbox_mocha-chai",
"version": "0.0.1",
"description": "A sandbox for configuring and using the mocha test framework and the chai assertion library",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/josh-egan/sandbox_mocha-chai.git"
},
"private": true,
"keywords": [
"mocha",
"chai"
],
"author": "Joshua Egan",
"license": "MIT",
"bugs": {
"url": "https://github.com/josh-egan/sandbox_mocha-chai/issues"
},
"homepage": "https://github.com/josh-egan/sandbox_mocha-chai"
}
Next let's install mocha. From the command line use:
npm install mocha --save-dev
Now let's write a command to run the tests. This can be done in various ways. I like to use npm scripts, so open up the package.json
file and add the following script:
{
"scripts": {
"test": "./node_modules/.bin/mocha ./src/math-module.specs.js --ui bdd --reporter progress"
}
}
I like to think of npm scripts as aliases for more complex scripts. So every time I want to run mocha, instead of typing the command above, all I have to type is npm test
. As a side node, npm has a couple of special commands, namely start
and test
, so for additional scripts, you would type npm run my-awesome-script
, with run
being the key addition there.
So let's break down the above script. We're calling the mocha cli and telling it which test files to execute and which options to use. First, mocha accepts one or more file glob patterns. The pattern above will run a single test file, namely math-module.specs.js
. The --ui bdd
option sets the user interface to bdd. There are a few user interfaces to choose from. Finally, the --reporter progress
option sets the reporter to the progress reporter. There are a number of built in reporters. There are also quite a few custom reporters that can be installed using npm. I wrote a small reporter that I like to use called the mocha-minimalist-reporter.
I'm only using a few of the mocha cli parameters in this intro, but a complete guide to mocha's cli usage is detailed here. So check it out if you need mocha to do something in particular for you.
Cool, so now we're ready to write some tests! Create a src
folder, and then create a file named math-module.specs.js
in the src folder. Here's what mine looks like:
describe('math module', function () {
describe('add', function () {
it('should correctly add positive numbers', function () {
throw "Not implemented..."
})
})
})
Save the file and go back to the console and run npm test
and you should see your first failing test!
Not super interesting yet, so let's introduce some assertions. mocha will work with any assertion library, so if you've got a favorite, you can use it. I've been using chai for a while, so we'll use it here. Let's start by bringing chai in using npm:
npm install chai --save-dev
Now let's use chai. At the top of the specs file, import and configure chai as follows:
var chai = require('chai')
var expect = chai.expect
Let me take a moment to just add that you might need to add some configuration immediately after the import statement.
var chai = require('chai')
// If failing tests don't show a stack trace, try using this setting
chai.config.includeStack = true
var expect = chai.expect
I once ran into an issue where my stack traces for failing tests were getting completely swallowed. Setting includeStack
to true solved the issue for me. It took me a long time before I finally discovered that this little configuration value was causing my problem. I'm still not sure why the stack trace completely disappeared, because normally the stack trace shows up even with that setting set to false, which is the default. But there was a lot going on in that project, including using babel and transpiling es6 and es7 code, so perhaps something just wasn't playing nice.
There are several assertion styles that chai supports. I like to use the expect
style. It is a bdd style, and I prefer 'expect' over 'should' because of this scenario:
var result = myObject.thisMethodReturnsUndefined()
expect(result).to.be.undefined //Works like a charm
result.should.be.undefined //Goes down in flames
So now that we've brought chai in, lets make an assertion in our test:
it('should correctly add positive numbers', function () {
var result = 3 + 3
expect(result).to.equal(5)
})
With that failing test written, run npm test
in the console and you should get a nice failure message describing the fact that 6 is not equal to 5.
Modify the test to make it pass, run it again, and enjoy the feeling that comes from seeing all of your tests pass. :)
Now that we've got our environment up and running, let's actually test some production code.
First, modify the specs file to bring in the math module by adding this to the top of the file:
var mathModule = require('./math-module')
Running the tests should fail because the math module doesn't exist yet, so create a file in the src folder named math-module.js
and give it some meaningful code like so:
console.log("i'm alive!")
Running the tests should pass at this point and you should see the output in the console.
So now let's update the test method to call the add method on the math module.
it('should correctly add positive numbers', function () {
var result = mathModule.add(3, 2)
expect(result).to.equal(5)
})
Running that test should fail because the method doesn't exist yet. So lets replace all the code in the math-module.js
file with this:
var mathModule = {
add: function (a, b) {
return a + b
}
}
module.exports = mathModule
Run the tests again, and we're passing now! Excellent! :)
If you've actually been following the steps I've been detailing and running the tests each time I've been running them, you'll agree that it's starting to get aggravating to change windows and run npm test
after every little change. Enter mocha's watch ability.
Let's add a script called test-watch
to our package.json
file.
{
"scripts": {
"test": "./node_modules/.bin/mocha ./src/math-module.specs.js --ui bdd --reporter progress",
"test-watch": "./node_modules/.bin/mocha ./src/math-module.specs.js --ui bdd --reporter progress --watch"
}
}
Note the --watch
flag at the end of the line. Now let's run the watch command using
npm run test-watch
Now, every time a file is saved the tests will get re-run! So much better. :) Add a test case and save the file just to prove it to yourself. My updated tests look like this now:
var chai = require('chai')
var mathModule = require('./math-module')
var expect = chai.expect
describe('math module', function () {
describe('add', function () {
it('should correctly add positive numbers', function () {
var result = mathModule.add(3, 2)
expect(result).to.equal(5)
})
it('should correctly add negative numbers', function () {
var result = mathModule.add(-3, -5)
expect(result).to.equal(-8)
})
})
})
Note that one limitation of the mocha watcher is that it currently doesn't pick up new files. So anytime you create a new file, you'll have to kill the watcher with CTRL+C
and then restart it.
Let's go back to the package.json
file and refactor it a bit. Notice how our scripts include so much duplication? Well, there's a nice little trick we can use to cut out the duplication. Modify the test-watch
script as follows:
{
"scripts": {
"test": "./node_modules/.bin/mocha ./src/math-module.specs.js --ui bdd --reporter progress",
"test-watch": "npm test -- --watch"
}
}
The --
double dash will run the specified script and then pass the additional parameters in. It's a pretty nice trick I picked up from a co-worker a few months ago. This can also be used to override parameters. For example, let's say you want a specific command to run on your build server, such as TeamCity. Well, if you're using TeamCity, you can npm install the mocha-teamcity-reporter and then create a script like so:
{
"scripts": {
"test": "./node_modules/.bin/mocha ./src/math-module.specs.js --ui bdd --reporter progress",
"test-watch": "npm test -- --watch",
"test-teamcity": "npm test -- --reporter mocha-teamcity-reporter"
}
}
Let's also change the file glob so that we run the tests in every file that ends with ".specs.js" instead of just looking for that single file.
{
"scripts": {
"test": "./node_modules/.bin/mocha ./**/*.specs.js --ui bdd --reporter progress",
"test-watch": "npm test -- --watch",
"test-teamcity": "npm test -- --reporter mocha-teamcity-reporter"
}
}
And now, what if we want to run a code coverage tool, such as istanbul? That script would look something like this:
{
"scripts": {
"test": "./node_modules/.bin/mocha ./**/*.specs.js --ui bdd --reporter progress",
"test-watch": "npm test -- --watch",
"test-teamcity": "npm test -- --reporter mocha-teamcity-reporter",
"test-coverage": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha"
}
}
But at this point, how do we pass in our mocha options? We can't do it from the command line, because the command line parameters go to the istanbul cli. Well, we can create a mocha.opts
file to set the base options. This way, we can specify our default options that will be applied any time mocha is run. Options passed in at the command line, for example in our test-teamcity
script, will override the options in the mocha.opts
file. It is a requirement that the options file be located at test/mocha.opts
, so you can't get creative on the file location or the file name. So let's create the file and add our defaults like so:
./**/*.specs.js
--ui bdd
--reporter mocha-minimalist-reporter
You may have caught that I went ahead and changed the reporter to the minimalist reporter. :) A quick
npm install mocha-minimalist-reporter --save-dev
was all that we needed to get the minimalist.
Now that we have the mocha.opts
file, we can update our scripts like so:
{
"scripts": {
"test": "./node_modules/.bin/mocha",
"test-watch": "npm test -- --watch",
"test-teamcity": "npm test -- --reporter mocha-teamcity-reporter",
"test-coverage": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha"
}
}
Go ahead and run the tests and try out the new scripts if you haven't already. They're working great.
You may have noticed that I have used the verbose parameters in all of the scripts (e.g. --watch
instead of -w
). I choose to do that because typing out the verbose parameter in the script I think is well worth a few extra keystrokes because of how easy it is to understand the script when you come back to look at it a couple months from now, or when someone new comes onto the project. No need to look up the docs to remember what -R
stands for if the option explicitly states --reporter
. When typing from the command line, I do use the shorter option, but for scripts I prefer to be verbose.
That concludes our basic configuration and usage of mocha and chai. As a reminder, all of the code for this post can be found on github. Check out the mocha docs and the chai docs for more details about what these libraries can do for you.