Running Mocha setup before each suite rather than

2019-03-23 08:14发布

问题:

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?

回答1:

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();
  });
});


回答2:

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");
  });
}


回答3:

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.