Mocha: global before/after helpers

Created on 6 Nov 2017  路  16Comments  路  Source: mochajs/mocha

this super useful feature is somehow still not implemented however lots of people would like to see it. It can be used for opening/closing db connection, creating test db, running migrations, starting selenium driver or doing any other action without calling mocha programmatically like we have to do now

the actual implementation may be different but the idea is to add before and after to mocha.opts to be included the same was as require currently does and has signature

export before () {
    // do some async calls
    return promise; // or call done()
}
needs-feedback

Most helpful comment

The new --file syntax should address this. Please let me know if it doesn't work for you, for whatever reason.

All 16 comments

this is verbose, but the only recommendation I have. Create file like so:

// my-global-hooks.js

let prom = null;

before('before all befores', function(){

   if(prom){
      return prom;
   }

   return prom = new Promise(...);

});

then in every test file, require this file:

require('./my-global-hooks.js');

describe('foo', function(){

});

if you do this, it will only load once

otherwise, you can do this if you to wrap in a function and pass data:

let loaded = false;
exports.loadGlobalBefores = function(someDataHere){

   if(loaded) return;
    loaded = true;

    let prom = null;
    before('before all befores', function(){

      if(prom){
         return prom;
      }

      return prom = new Promise(...);

   });
}

yes there are numerous workarounds i even can create pretest script in package.json but still it would be good to have native global hook

I agree...maybe in mocha.opts you could have a relative file path to the globals file and mocha would load that first

This is, in fact, well supported already unless I'm missing something fundamental about what's being requested. Not only do global hooks exist, but they actually don't even require any special files, usage patterns or workarounds -- just put before (or after, or the ...Each versions) outside any describe in some file that is among the test files, it doesn't even need to be the first to run and it doesn't need any other file to require it.

There is a trick to managing test files if you need to use a feature like this but also want to be able to specify the path to a particular file or files to run while still having all test files be run by default; the pattern is not specific to global-hooks files and is relevant/useful in other cases as well:

  1. Put any filepaths that need to always run inside mocha.opts
  2. Put the default way to run all the tests (e.g. --recursive path/to/folder or test/**/*.js) in the test script in package.json
  3. Run npm test for the default or mocha path/to.js specific/test.js to override it with one or more files instead of all.

@ScottFreeCode yeah the problem with that is when you want to run just one test file, then you have to think about where you put that global hook. It's better when each file declares its dependencies instead of having a random global hook _somewhere_.

@ORESoftware That's a fair point; if you prefer to make it explicit in each test file that they require that global behavior, requireing it would be the right way to do it*. Another alternative (or a stronger version of the same idea, depending on how you look at it) is to avoid having the hooks really be global in the first place and explicitly put them in each suite that needs them (even if it's by a function call to avoid duplicating the actual hook code), which gives you flexibility if you ever decide some suites don't -- or do but need it to be a little different, etc.

*(I'm still not sure the top-level let variables are needed, at least usually -- Node's require caches modules rather than running twice and I'm not aware of a system that does otherwise, but if there were, wouldn't running the module twice recreate those variables since they're not global? I might be wrong about that for some specific cases though.)

@ScottFreeCode you are thinking only about a case with db but the problem is much wider.
Lets use real example. there is a test engine called http://webdriver.io/ . they have integration with mocha but its really poor because they are invoking mocha programmatically after selenium driver is started. and somehow they have problem with only/skip functions, because they are parsing js files. i'm not sure about details but i believe this can be avoided by reversing dependencies.

Could this be the solution for your problem?
https://github.com/airbnb/mocha-wrap

see skip/only section. they ran into exact same problem.

The new --file syntax should address this. Please let me know if it doesn't work for you, for whatever reason.

@boneskull maybe a stupid question but I have async code in my init file, how do I tell it to wait until all the async code is finished executing? (Node 9.6.1)

@BorisKozo just like any other hook.

you can add a top-level before hook in your init file:

// init.js
before(function (done) {
  // do async stuff
  done();
});

// or using a Promise

before(function() {
  return Promise.resolve().then(/*..*/);
});

Thanks!

And what if it takes more as 2000 ms?

before(function (done) {
    setTimeout(() => {
        console.log('init');
        done();
    },3000);
});

describe('AsynchTest', () => {
    it(`AsynchTestIt`, () => {
        console.log('it');
    })
});

Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
at ....

It is possible to split it:

before(function (done) {
    console.log('init11');
    setTimeout(() => {
        console.log('init12');
        done();
    },1000);
});

before(function (done) {
    console.log('init21');
    setTimeout(() => {
        console.log('init22');
        done();
    },1000);
});


describe('AsynchTest', () => {
    it(`AsynchTestIt`, (done) => {
        console.log('AsynchTest1');
        setTimeout(() => {
            console.log('AsynchTest2');
            done();
        },1000);
    })

    it(`synch`, () => {
        console.log('synch');
    })
});

init11
init12
init21
init22
AsynchTest1
AsynchTest2
synch

@ujja use --timeout; please read the docs at https://mochajs.org and/or ask in chat, https://gitter.im/mochajs/mocha

Was this page helpful?
0 / 5 - 0 ratings

Related issues

enigmatic00 picture enigmatic00  路  3Comments

adamhooper picture adamhooper  路  3Comments

EdvinOlofsson picture EdvinOlofsson  路  3Comments

helloravi picture helloravi  路  3Comments

robertherber picture robertherber  路  3Comments