Unit testing CommonJS code under Node has never been easier. Here is how to start unit testing your package in 10 seconds. Let us write a small module to add two numbers
1 | module.exports = function add(a, b) { |
Assuming we already have package.json
, do the following (you can copy and paste the commands
directly from the blog post)
second 1
Install the Mocha testing framework, save it under devDependencies
npm install --save-dev mocha
I love Mocha and recommend it for JavaScript testing if you like Behavior-Driven Design (BDD) testing pattern.
second 2
Create a folder for the test files (specs) and add first empty file
md test
git touch add-spec.js
second 3-5
Paste the BDD boilerplate into the add-spec.js
file
1 | describe('add', function () { |
second 6
Run mocha on npm test
command by setting the command in the package.json
1 | "scripts": { |
I prefer the spec
runner, since it creates nice hierarchy of passing / failing unit tests
second 7
Execute the tests
npm test
Produces the following output
$ npm test
> [email protected] test /js/mocha/add
> mocha -R spec test/*-spec.js
add
✓ is a function
1 passing (5ms)
Beautiful!
Bonus: second 8
It would be nice if the tests were rerun automatically as we were changing the source code.
Mocha has built-in watch feature, I prefer creating a separate npm script command to enable it
(passing command line options using --
separator requires Node > 0.11)
1 | "scripts": { |
To run the unit tests and watch for file changes use npm run watch
Bonus: second 9-10
Unlike Jasmine, Mocha does not come with built-in matchers. In the example
above I used the simple console.assert
that throws an exception if the predicate is false. For more
power I include my lazy-ass assertion function
together with check-more-types predicate collection.
npm install --save-dev lazy-ass check-more-types
The same spec file will look like this
1 | require('lazy-ass'); |
If the predicate fails, la
(short alias to lazyAss
) will intelligently stringify all arguments,
making sure the failure context is available.
Bonus: second 11-14
To rerun unit tests on each commit to avoid breaking the local master
just install pre-git and connect it to the npm test
command
npm install --save-dev pre-git
1 | "scripts": { |
Every commit will now run npm test
and only commit if the unit tests pass.
Bonus: second 15-25
Running unit tests locally is nice, but making sure they run on a separate continuous integration
machine is even nicer. If your project is open source and hosted on Github, enable TravisCI integration
by going to your profile page https://travis-ci.org/profile/
Add the Travis config file .travis.yml
to your repo
1 | language: node_js |
The Mocha tests will be rerun on each push to the Github, as well as any pull requests. To let everyone know the tests are there and passing, add a badge to the README.md file
[![Build status][ci-image]][ci-url]
[ci-image]: https://travis-ci.org/<username>/<project name>.png?branch=master
[ci-url]: https://travis-ci.org/<username>/<project name>
Substitute your own Github username and the repo's name
Bonus: seconds 25-35
BDD specs have too much boilerplate - everything is a function, which is verbose in ES5. Luckily we can easily write the unit tests in ES6 and use arrow notation to avoid extra characters
Add Babel preprocessor to the Mocha command
1 | npm install --save-dev babel |
1 | "scripts": { |
Now modify test/add-spec.js
to use arrow functions, the tests and assertions become one-liners
1 | require('lazy-ass'); |
Note: Babel 6 has changed the instructions, and now requires installing a transformation plugin,
and creating a .babelrc
file. Read the instructions
Bonus: seconds 35-40
You can add snapshot testing to any JS framework using snap-shot library. All you need is to install and start using it.
1 | npm install --save-dev snap-shot |
1 | const snapshot = require('snap-shot') |
The first time the test runs, it saves the object result
as a JSON file.
You should add this JSON file to the Git repo. Next time the test runs,
it will compare the new result to the saved object and raise an error
if they differ. Read more about snapshot testing.
hint: for highly dynamic data you can snapshot object schema using schema-shot or some other snapshot testing lib
Updates
Pick snapshot library
There are multiple unit testing libraries, I wrote some thoughts on how to pick one.
CoffeeScript
You might consider writing unit tests in a language other than JavaScript.
For example CoffeeScript is a language that
compiles into JavaScript on the fly via Node require hook. Loading .coffee
files into Mocha and transpiling them is very simple. For example
1 | function add(a, b) { |
and the spec file
1 | add = require('./math').add |
Install mocha
and coffeescript
first
1 | $ npm i -D mocha coffeescript |
and register CoffeeScript require hook
1 | { |
CoffeeScript tests now run.
1 | $ npm t |
Perfect!
Alternatively, you can specify that CoffeeScript can only transpile .coffee
files
1 | "scripts": { |
The result is the same.
Use Mocha options file
If there are a lot of CLI options to pass to the Mocha runner, you can place
them into a file. For example, create a folder test
and add a file
mocha.opts
there with CoffeeScript compiler options.
1 | $ ls |
1 | $ cat test/mocha.opts |
And now we can use simple test command in package.json
because the test folder
"test" and the Mocha options are all going to be found automatically!
1 | { |
TypeScript
You can write your tests using TypeScript (see my
intro to TypeScript blog post). Let us install the
TS compiler and bring type definitions for Mocha right away. I will also
add ts-node that has TypeScript
require hook.
Then we will initialize a TypeScript configuration
(if the project already does not have one). We will need to set the
allowJs
option to true
in the tsconfig.json
for TypeScript spec files
to be able to load math.js
file.
1 | $ npm i -D ts-node typescript @types/mocha |
Let us write simple TypeScript spec file
1 | import {add} from '../math' |
Add TypeScript compiler to the Mocha options file
1 | test |
and enjoy CoffeeScript and TypeScript specs running!
1 | $ npm t |
Perfect.