Quick TDD setup with Node, ES6, Gulp and Mocha

Having practised Test Driven Development for the last couple of years, I’ve found testing one of the most frustrating things about Node compared to other programming languages.

Laravel, a framework I have used for the past few years, has come with a gulp extension called Elixir since version 5.0 which is a massive timesaver. Simply type gulp tdd  into the command line and your tests will run automatically. Luckily, this effect isn’t hard to replicate.

Of course, there are a number of testing frameworks out there for testing NodeJS applications which go beyond the scope of this post. Suffice to say, I’ve tried a few and combining the Mocha testing framework with Chai’s assertion library has been the one that stuck with me.

Setting up

Getting up and running is easy with Gulp, simply load in the gulp-mocha package using npm. This will load the dependencies and add to the devDependencies options to your package.json file and make sure they are available.

npm install --save-dev gulp gulp-mocha babel-register babel-preset-es2015 chai

Inside package.json you should now see your dependencies. By requiring babel-register, we are able to test ES6 features straight away.

At this point, we also need to register the es2015 default. This can be done by creating a .babelrc file in the root directory of the project or by adding our settings to the package.json file.   For berevity, let’s add the es2015 preset into our package.json file. Once finished, your package.json file should resembling the contents below.

package.json
{
"devDependencies": {
"babel-preset-es2015": "^6.9.0",
"babel-register": "^6.9.0",
"chai": "^3.5.0",
"gulp": "^3.9.1",
"gulp-mocha": "^2.2.0"
},
"babel": {
"presets": ["es2015"]
}
}

Some Code to Test

Now, let’s create something to test. As a simple example, let’s create a function that will sum the list of arguments. This is a good demonstration of the spread operator that exists within ES6.

src/sum.js
export default function sum(...figures) {
return figures.reduce((total, current) => {
return total + current;
});
}

Running Tests

Now we have some code, let’s write out first test. Mocha can be run directly via command line by installing the mocha package globally in npm.

npm install -g mocha

Because we’re using ES6 features, we will need to use the --compilers option when running our tests otherwise node will throw an Unexpected token "import" error. Here we are telling mocha to use babel core when it encounters any js files.

mocha --compilers js:babel-core/register

Running this now, we should receive an error telling us that mocha cannot resolve the path or pattern.

Writing a Test

By default, mocha looks for tests in the test/ directory. You can change this by adding a specific path or pattern to your mocha call but for now let’s stick with the defaults. Let’s create a simple test.

test/sum.spec.js
import {expect} from 'chai';
import sum from '../src/sum.js';

describe('src/sum.js', _ => {
it('should add 1 + 1 to make two', () => {
let result = sum(1,1);
expect(result).to.equal(2);
})
})

If we have done everything right, we should be able to run our test via the command line and get a successful response.

mocha --compilers js:babel-core/register
src/sum.js
✓ should add 1 + 1 to make two

Automating with Gulp

Now we’ve got our test suite, we can use gulp to automatically run the suite.

To access gulp via the command line, we will need to install it globally using npm.

npm install -g gulp

Once installed, we can run any task we have defined in the gulpfile.js file via the command line. For example, to run the taskname task we would type the following.
gulp taskname

If no task name is supplied, gulp will attempt to run a task named default.

Let’s create a task within our gulpfile to allow us to run our test suite with mocha.

gulpfile.js
var gulp = require('gulp'),
mocha = require('gulp-mocha'),
babel = require('babel-register');

gulp.task('test', function() {
return gulp.src(['test/*.js'])
.pipe(mocha({
compilers:babel
}));
});

Firstly, we’re importing the gulp, mocha and babel dependencies in to our gulp file for use. Next, we’ve created a task called test which will use the gulp.src function to find all files matching the supplied pattern and then pipe the contents into our mocha testing framework. By passing the babel dependency through will ensure that mocha will interpret our ES6 code correctly.

If we run this gulp task, we should receive a message telling us our tests are passing.

[09:00:00] Using gulpfile ~/_/gulpfile.js
[09:00:00] Starting 'test'...

src/sum.js
✓ should add 1 + 1 to make two

1 passing (6ms)
[09:00:00] Finished 'test' after 245 ms

Great, but right now we’re no further forward than when we were running mocha manually. By utilising gulp.watch(), we can listen for changes in our file and subsequently run our gulp tasks. Let’s take advantage of this by creating a new task which will watch for changes in our files and run our test suite.

gulp.task('tdd', function() {
return gulp.watch(['src/*.js','test/*.js'], ['test']);
})

When changes are made to any of the .js files in the src/ or test/ directories our entire test suite will re-run and instantly identify any problems with our changes.

Extra Credit – Single Tests

This approach is great for smaller test suites but as your TDD project grows, running hundreds to thousands of tests for each change can grind your productivity to a halt. gulp.watch also emits a change event that passes through information on the file that has been changed. We can use this to listen for changes to our test suites and only run the suite that has been changed. For this I will create a tdd-single task.

gulp.task('tdd-single', function() {
return gulp.watch('test/*.js')
.on('change', function(file) {
gulp.src(file.path)
.pipe(mocha({
compilers: babel
}))
});
});

This is perfect for unit testing scenarios where you are individual units of source code.

Conclusion

TDD is becoming a more and more important part of the development process for ensuring quality. Gulp and Mocha work hand in hand to allow you to quickly create a solid testing environment and will save you headaches in the long run when trying to debug your code.

The source for this blog post is available on github.

Happy testing!