How can I make Mocha load a helper.js file that defines global hooks or utilities?

Mocha does not have any notion of a special file named helper.js that it would load before other files.

What you are trying to do works when you run mocha --recursive because of the order in which Mocha happens to load your files. Because helper.js is one level higher than the other files, it is loaded first. When you specify an individual file to Mocha, then Mocha just loads this file and, as you discovered, your helper.js file is not loaded at all.

So what you want to do is load a file such that it will set top level ("global") hooks (e.g. before, after, etc.). Options:

  1. You could use Mocha programmatically and feed it the files in the order you want.

  2. You could force yourself to always specify your helper file on the command line first before you list any other file. (I would not do this, but it is possible.)

  3. Another option would be to organize your suite like I've detailed in this answer. Basically, you have one "top level" file that loads the rest of the suite into it. With this method you'd lose the ability of running Mocha on individual files, but you could use --grep to select what is being run.

You cannot use the -r option. It loads a module before running the suite but, unfortunately, the loaded module does not have access to any of the testing interface that Mocha makes available to your tests so it cannot set hooks.


What I do is create a test/test_helper.js file, which exports all the helpers I create:

// test/test_helper.js    
module.exports = {
    MyHelper: require('./helpers/MyHelper')
}

Then I require the helper on any test I need to use it:

// test/spec/MySpec.js
var helper = require('../test_helper');

// Or if you need just "MyHelper"
var myHelper = require('../test_helper').MyHelper;

describe('MySpec', function () {
   // Tests here...
});

I prefer the above approach because its easy to understand and flexible. You can see it in action here in my demo: https://github.com/paulredmond/karma-browserify-demo/tree/master/test


First, I would definitely use mocha.opts so that you don't have to include the options you want every time. As pointed out, one option is to use --grep, but I am not a huge fan of that personally. It required you name everything in an overly simplistic way. If the before hook is NOT async you can use --require in your mocha.opts. e.g.

#mocha.opts
--recursive
--require test/helpers.js

It sounds like this wouldn't work for you because you want global after hook as well. What I have done is I just call the full test suite every time, but if I am in the middle of deving and only want to test one suite, or even one specific test, I use the exclusivity feature, only https://mochajs.org/#exclusive-tests. You can make it it.only('... or describe.only('... If you do this it looks through all tests and sets up exactly like your full test harness would, but then only executes the test or suite you have specified.

Now you can include those global hooks no problem. @Louis mentions that your helpers.js are loading in the proper order only coincidently. That is not true. If you place any hooks outside of a describe block, it automatically becomes a global hook. This can be accomplished by either putting it in its own file

// helpers.js
before(function() { console.log('testing...'); });

or within a test file

// some.spec.js
before(function() { console.log('testing...'); });

describe('Something', function() {
  it('will be tested', function() {
  ...
  });
});

Obviously, I think putting it in its own file is cleaner. (I called it hooks.js). Point is, this is not a result of the order in which files were loaded.

Just one gotcha that might be obvious to other but I struggled with briefly -- hooks not placed in a describe block are ALL global. They are not directory specific. So if you copy helpers.js into a sub-directory of tests, the before and after hook will now fire twice. Also, if you place a beforeEach hook in there, it will fire before every single test, not just those tests in that directory.

Anyway, I know this post is a bit old, but hopefully this will help others having similar issues.