5

I am writing a node application with mocha and chai. Some of the tests call an external API for integration tests, which might take up to 90sec to perform the action.

In order to cleanup properly, I defined an afterEach()-block, which will delete any generated remote resources, in case an expect fails and some resources weren't deleted.

The tests themselves have an increased timeout, while the rest of the tests should retain their default and small timeout:

it('should create remote resource', () => {...}).timeout(120000)

However, I can't do the same with afterEach().timeout(120000), because the function does not exist - nor can I use the function ()-notation due to the unknown resource names:

describe('Resources', function () {
  beforeEach(() => {
    this._resources = null
  })

  it('should create resources', async () => {
    this._resources = await createResources()
    expect(false).to.equal(true)   // fail test, so...
    await deleteResources()        // will never be called
  })

  afterEach(async() => {
    this._resources.map(entry => {
      await // ... delete any created resources ...
    })
  }).timeout(120000)
})

Any hints? Mocha is version 4.0.1, chai is 4.1.2

Lars
  • 5,757
  • 4
  • 25
  • 55

2 Answers2

6

The rules are same for all Mocha blocks.

timeout can be set for arrow functions in Mocha 1.x with:

  afterEach((done) => {
    // ...
    done();
  }).timeout(120000);

And for 2.x and higher it, beforeEach, etc. blocks are expected to be regular functions in order to reach spec context. If suite context (describe this) should be reached, it can be assigned to another variable:

describe('...', function () {
  const suite = this;

  before(function () {
    // common suite timeout that doesn't really need to be placed inside before block
    suite.timeout(60000); 
  }); 
  ...
  afterEach(function (done) {
    this.timeout(120000);
    // ...
    done();
  });
});

Mocha contexts are expected to be used like that, since spec context is useful, and there are virtually no good reasons to access suite context inside specs.

And done parameter or promise return are necessary for asynchronous blocks.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • As already written, neither does work for me. I cannot use the `function(done)` due to information that needs to be passed on to the afterEach-method by the run test about the resources created. When I try to run the `(done) => {}` or the `async () => {}` notation, mocha will fail to start with `TypeError: Cannot read property 'timeout' of undefined` – Lars Nov 14 '17 at 14:40
  • Again, you cannot use `this.timeout(120000)` with arrow functions here. And I'd expect `afterEach((done) => { .. }).timeout(120000)` to work. – Estus Flask Nov 14 '17 at 14:44
  • It should be noticed that `afterEach((done) => { .. }).timeout(120000)` works in Mocha 1.x, this is my usual testing rig. – Estus Flask Nov 14 '17 at 14:53
  • I've updated the example code and the versions. Why are you still using Mocha 1 if I may ask? – Lars Nov 14 '17 at 14:58
  • Because higher versions provided a lot of breaking changes I wasn't comfortable with. I don't remember this one but I guess it was one of them. Sorry if this was misleading. From what I see in your code you have XY problem then. Why do you use suite context (describe `this`)? Is _resources supposed to be persistent among several specs within this suite? – Estus Flask Nov 14 '17 at 15:24
  • no, it's just a helper. I could wrap every test body in try-catch-blocks or hack around with `let self=this`, etc, but I just didn't think that there is no way to set the timeout on afterEach. Guess I'll just open a bug report. Thanks for your time! – Lars Nov 14 '17 at 22:38
  • A bug report is hardly worth it, it will likely be overruled it's conventional to set up test variables on spec context (it, afterEach `this`) and not suite context (describe `this`), so the latter is useless inside spec blocks. This is a common trait of both Mocha and Jasmine. Actually, I can't think of good reasons to access suite context inside spec blocks (it creates a risk of test cross contamination). That's why I assumed that there is XY problem. I updated the answer to reflect this. – Estus Flask Nov 14 '17 at 23:13
3

If you need to use dynamic context you have to use normal function.

describe('Resources', function () {
  // ...
  afterEach(function (){
    this.timeout(120000)  // this should work
    // ... delete any created resources ...
  })
})
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • This is not possible, because the resources created by a test do not have a previously known name - so I need to pass information about those resources from the ran test. – Lars Nov 14 '17 at 14:41
  • @Lars I don't see how what you have posted is even related to the question :) You need to replace arrow function w/ normal function to make use of dynamic context passed by mocha. – Yury Tarabanko Nov 14 '17 at 14:44
  • yes, but that won't work for me. I've updated my example code to reflect that. – Lars Nov 14 '17 at 14:58
  • 1
    @Lars "- nor can I use the function ()-notation due to the unknown resource names" this doesn't make sense to me. How `async () => {// blah}` is different from `async function() { // blah}` wrt what you have called "resource names". – Yury Tarabanko Nov 14 '17 at 15:08