Testing in ES6 with Mocha and Babel 6

This guide is part of The Complete Guide to ES6 with Babel 6 series. If you’re having trouble upgrading to Babel 6, start with Six Things You Need To Know About Babel 6.

So you’ve written a useful little app with ES6, and being the excellent developer that you are, you want to test it. You’ve got some experience testing with Mocha, so you write a few tests and run them. And bam, nothing works. Why? Because by default, Mocha only knows ES5. But luckily, teaching Mocha ES6 only takes about a minute and 30 seconds!

Dependencies

Skip this section if you’ve already installed Babel 6 and any required presets/plugins.

Babel 6 doesn’t play well with its younger self, so start by removing any older Babel packages from package.jsonbabel, babel-core, etc. Make sure you npm uninstall <package-name> each one to remove them from node_modules too.

We’ll need to install the babel-core package to get access to its require hook:

npm install babel-core --save-dev

Once complete, you’ll need to install the presets and plugins you want to use to transform your code. Start with babel-preset-es2015 – Babel’s collection of ES6 transforms. If you want to use JSX in your tests, you’ll also want babel-preset-react. You can also use experimental ES7 features by adding babel-preset-stage-0, but please take a long, hard think about your life before you do.

# For ES6/ES2015 support
npm install babel-preset-es2015 --save-dev

# If you want to use JSX
npm install babel-preset-react --save-dev

# If you *must* use experimental ES7 features
npm install babel-preset-stage-0 --save-dev

Configuring Babel

Of course, just having Babel’s packages installed isn’t enough. We still need to tell Babel to use them!

For the Webpack and Babel CLI episodes, we were able to do this with configuration specific to each tool. But while Mocha let’s us run our tests through Babel, it provides no way to configure it. This means we’ll need to configure Babel with a .babelrc file in the project’s root directory:

{
  "presets": ["es2015"]
}

If you want to use the react or stage-0 presets, just add them to the presets array.

Hint: Global configuration

Unfortunately, .babelrc is shared between all tools which can’t set their own configuration. For example, Gulp also reads .babelrc when used with a gulpfile.babel.js. Plan accordingly, and try not to put anything in there which is going to surprise you later on.

Running ES6 tests

Once Babel is installed and configured, all that is left is to tell Mocha to use it!

mocha --compilers js:babel-core/register

Assuming you haven’t used any of ES6’S new built-in’s, your tests should pass with flying colours. But what if you have?

Now my tests compile, but fail?!

While the babel-core/register script transforms your tests into ES5 JavaScript, it takes care not to change the global environment. That means that if Promise, Map, Set or Object.assign are missing from your JavaScript environment, they’ll stay missing.

For testing, this has a definite upside: your tests see what the consumer sees. But maybe you’re package isn’t meant to be published, or maybe you’re testing an app instead of a library. In this case, you don’t care about the global environment. And in all likelihood, you want to use ES6 — not a confusing subset of it. So install the built-ins with babel-polyfill:

npm install babel-polyfill --save

And then require them by adding it to the top of your tests:

import 'babel-polyfill'

But keep in mind that if you’re publishing a library, this will eventually cause you to publish code which will not run on the majority of JavaScript runtimes. Loading babel-polyfill in your tests won’t load it for your consumers too. Be careful.

Tests vs Source

Talking about published code, let’s say you’re writing a library which you’d like to publish to NPM. Because you’ve done your research, you know that your published source should be ES5. And to make this happen, you’ve set up your project to compile your src directory’s files to a lib directory before publishing.

Naturally, your tests import the lib code which you’re actually publishing, not the original src files. And while this may go without saying, make sure you compile your code before testing!

Assuming you’ve followed part 1 and configured npm run compile to compile your source, that means running your tests after the compilation step:

npm run compile && mocha --compilers js:babel-core/register

Of course, even though you know you should run your tests like this, you may well forget. And that’s why you should…

Configure your tests in package.json

By adding a test entry to the scripts object in package.json, NPM will know to run the above test command whenever we call npm test from the terminal:

"scripts": {
    "test": "npm run compile && mocha --compilers js:babel-core/register"
},
Hint: Upgrading from previous Babel versions

Babel 5’s register script was located in the babel package, not the babel-core package. So if you’re upgrading, make sure to update the package name in the test script in package.json!

Examples

For an example of a package with tests run with Mocha and Babel 6, see react-pacomo.

OK, it works! But for how long?

Here’s a little story: while writing these guides, I learned that the require hook in babel-core used by Mocha is already set to be deprecated (it will now be placed in a new package called babel-require). This is despite the fact that Babel 6 only introduced the current way of doing things a few weeks ago.

Staying up to date in our JavaScript-driven world can be tough — and that’s why I provide my Newsletter! Subscribe to receive my latest guides to writing small apps with React. And in return for your e-mail, you’ll immediately receive three bonus print-optimised PDF cheatsheets – on React (see preview), ES6 and JavaScript promises. All for free!

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

One more thing – I love hearing your questions, offers and opinions. If you have something to say, leave a comment or send me an e-mail at james@jamesknelson.com. I’m looking forward to hearing from you!

Read More

Related Projects

10 Comments Testing in ES6 with Mocha and Babel 6

  1. Crogo

    According to babel-core’s source, babel-register will be the new standard for the require hook :

    `npm install babel-register –save-dev`
    `mocha –compilers js:babel-register`

    Works fine for me !

    Reply
  2. Jithin

    With babel 5.x. I was able to use require hook via mocha –require option.
    I used to have a js file where I do require(‘babel-core/register’, options).
    Is that approach changed with babel 6.x ? I was relying on babel option “resolveModuleSource” to make by tests play nice with System.js plugins I was using.

    Reply
    1. Chris

      yes you can. Just add the watch option:
      $ mocha –compilers js:babel-core/register –require ./test/test_helper.js –recursive –watch”

      Reply
  3. Frank

    From what I gather, babel recommends using babel-runtime for libraries rather than babel-polypill. I understand the argument that tests are not going to be shared with the consumers though is there a way to leverage babel-runtime with Mocha instead of needing the polyfills in the tests ?

    Reply
  4. cmdcolin

    Thanks much for the article

    I was also seeing errors during mocha tests saying “Unexpected token export”, which were pointing to when external modules that contained es6 code. I fixed this error by adding this to the top of my mocha test script

    require(“babel-register”)({
    ignore: false
    });

    Source: See note about node_modules being ignored by babel-register here https://babeljs.io/docs/usage/require/

    Not sure how ideal this setup is but it worked for me!

    Reply
  5. Kevin

    My tests fail because babel changes certain variables to be prefixed with an underscore.

    So ‘myVar’ will become ‘_myVar’.

    My problem is that when I transpile my tests with babel, the same rules don’t always apply.

    So in a test that injects a variable called myVar into an angular controller that has been changed to _myVar, everything breaks.

    I can’t figure out how to fix this.

    Reply
  6. Larry

    Is there any way to tell Babel not to parse certain files: eg:
    SyntaxError: /Users/larry/Projects/Dynamic-Traveler-2/app/assets/base/interface/move-folder.png: Unexpected character ‘�’ (1:0)
    �PNG

    The component I am testing imports images

    Reply

Leave a Reply

Your email address will not be published.