39

Using NodeJS and Mocha for testing. I think I understand how before() and beforeEach() work. Problem is, I'd like to add a setup script that runs before each "describe" rather than before each "it".

If I use before() it will run only once for the entire suite, and if I use beforeEach() it will execute before every single test, so I'm trying to find a middle ground.

So, if this is my test file:

require('./setupStuff');

describe('Suite one', function(){
  it('S1 Test one', function(done){
    ...
  });
  it('S1 Test two', function(done){
    ...
  });
});
describe('Suite two', function(){
  it('S2 Test one', function(done){
    ...
  });
});

I'd like to have "setupStuff" contain a function that runs before 'Suite one' and 'Suite two'

Or, in other words, before 'S1 Test one' and 'S2 Test one' but NOT before 'S1 Test two'.

Can it be done?

4 Answers 4

28

There's no call similar to beforeEach or before that does what you want. But it is not needed because you can do it this way:

function makeSuite(name, tests) {
    describe(name, function () {
        before(function () {
            console.log("shared before");
        });
        tests();
        after(function () {
            console.log("shared after");
        });
    });
}

makeSuite('Suite one', function(){
  it('S1 Test one', function(done){
      done();
  });
  it('S1 Test two', function(done){
      done();
  });
});

makeSuite('Suite two', function(){
  it('S2 Test one', function(done){
    done();
  });
});
4
  • 1
    This works perfectly (when not using skip or only), do you know how this would work if you wanted to add skip or only (on describe level)? Commented Jun 26, 2018 at 7:47
  • I came up with the same think but unfortunatelly, Jetbrains IDE will recognize the file as JS and not as MOCHA. :-/
    – Amio.io
    Commented Jan 18, 2019 at 19:09
  • 1
    How would one pass values that are instantiated in the before to all of the tests? Ideally you would run the function to be tested in the before and then have a set of it blocks to evaluate the return value and state. You also might want more custom before and after hooks for each suite in addition to the base set of before and after. beforeEach and afterEach don't cut it since they only operate on it blocks and not describe blocks.
    – mienaikoe
    Commented Oct 17, 2021 at 21:47
  • For me, the before didn't run. I just included the before code directly before calling test() Commented Sep 20, 2022 at 8:40
16

you can also do it in this more flexible way:

require('./setupStuff');

describe('Suite one', function(){
  loadBeforeAndAfter(); //<-- added
  it('S1 Test one', function(done){
    ...
  });
  it('S1 Test two', function(done){
    ...
  });
});
describe('Suite two', function(){
  loadBeforeAndAfter();//<-- added
  it('S2 Test one', function(done){
    ...
  });
});
describe('Suite three', function(){
  //use some other loader here, before/after, or nothing
  it('S3 Test one', function(done){
    ...
  });
});

function loadBeforeAndAfter() {
  before(function () {
    console.log("shared before");
  });
  after(function () {
    console.log("shared after");
  });
}
1
  • 1
    I started ready the answers and though Louis had the perfect solution, however it stopped me from using skip and only. This solution does the same as his, however it allows me to keep using skip and only. Commented Jun 26, 2018 at 7:50
1

@ya_dimon 's solution is working, but If you want to wrap the callback function of it, and pass parameter to it as follow.

function dynamicTestCase(a) {
  return function(done){ console.log(a); } // a is undefined
}

describe("test", function(){
    before(function(){
        a = 8;
    });
    it('POST /verifications receiveCode', dynamicTestCase(a)); // a is undefined
})

Why a is undefined? Because it is executed before before in describe. In this case, @ya_dimon 's solution could not work, but you could make It done trickly as following.

function dynamicTestCase(a) {
  return function(done){ console.log(a()); } // a is delayed pass! a = 8, remember change a to a()
}

describe("test", function(){
    before(function(){
        a = 8;
    });
    it('POST /verifications receiveCode', dynamicTestCase(() => a)); // a is delayed pass!
})

Hope this help to figure out the execution sequence.

1
  • even without my solution, in general you should not write such tests, as you said already, because it will collect all the passed functions first. But good info anyway.
    – ya_dimon
    Commented Jan 26, 2022 at 16:23
0

I have found this approach worked for me, it patches all describe suites.

function suitePatches()
{
    before(function()
    {
        // before suite behaviour
    });
    after(function()
    {
        // after suite behaviour
    });
}

let origDescribe = describe;
describe = function(n,tests)
{
    origDescribe(n,function()
    {
        suitePatches();
        tests.bind(this)();
    });
}
let origOnly = origDescribe.only;
describe.only = function(n,tests)
{
    origOnly(n,function()
    {
        suitePatches();
        tests.bind(this)();
    });
}
describe.skip = origDescribe.skip;

Differences from the other answers are:

  • The use of bind to call the tests which ensures that if they call functions on this, such as this.timeout(1000) will still work.
  • Handling .skip and .only means that you can still use those on your suite, eg describe.skip to temporarily suppress suites.
  • Replacing the describe function by name allows for a less intrusive injection.
    • This may not be to everyone's taste, in which case obviously an alternative function name can be used whilst still making use of the correct handling of calling the tests and only and skip.

Not the answer you're looking for? Browse other questions tagged or ask your own question.