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

I have a file named test/helper.js that I use to run Mocha tests on my Node.js apps. My tests structure looks like:

test/
test/helper.js    # global before/after
test/api/sometest.spec.js
test/models/somemodel.spec.js
... more here

The file helper.js has to be loaded because it contains global hooks for my test suite. When I run Mocha to execute the whole test suite like this:

mocha --recursive test/

the helper.js file is loaded before my tests and my before hook gets executed as expected.

However, when I run just one specific test, helper.js is not loaded before the test. This is how I run it:

mocha test/api/sometest.spec.js

No global before called, not even a console.log('I WAS HERE');.

So how can I get Mocha to always load my helper.js file?


Solution 1:

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.

Solution 2:

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

Solution 3:

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.

Solution 4:

Late addition to the answer:-

Mocha (v7.0.0 as of writing) support specifying file as an option:-
As per the docs

  --file               Specify file(s) to be loaded prior to root suite
                       execution 

.mocharc.json


{
    "watch-files": [
       "test/**/*.js"
    ],
    "recursive": true,
    "file": "test/setup"
}

./test/setup.js

const request = require('supertest');
const app = require('../app'); // express app
global.request = request(app);

A Worthy Mention:

I found that the above setup loaded all .js files anyway, probably because of the mocha config extension which is set to js by default. Since I had the convention of naming all tests file with .spec.js, I can ignore other files by adding "ignore": ["test/**/!(*.spec.js)"]