0

Test cases(Test1, Test2) execute before promise get the data. This is the file mockExecution.js

describe('AC 1: This is suite one', ()=>
{
    before((done)=>
    {
        promiseResp.then((data) => {
            console.log("i am in the promise");
            responseData = data;
            process.exit(0);
        }, (err) => {
            console.log('promiseResp.err', err);
            process.exit(1);
        })
        done();
    })
    it('Test1', (done)=>
    {
        expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
        done();
    });

    it('Test2', (done)=>
    {

        expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
        done();
    });

});

PromiseResp inside the Before block doesn't execute. Therefore "responseData" variable doesn't have data and it throws test case failed. I guess there is some asynchronous time issue, but don't know how to resolve it and also where do i put this "process.exit(0)". Below is the actual output:

AC 1: This is suite one
I am in the before
    1) Test1
    2) Test2


  0 passing (7ms)
  2 failing

  1) AC 1: This is suite one
       Test1:
     TypeError: Cannot read property 'measure' of undefined
      at Context.it (QA/mockExecution.js:160:29)

  2) AC 1: This is suite one
       Test2:
     TypeError: Cannot read property 'measure' of undefined
      at Context.it (QA/mockExecution.js:167:29)

[process business logic and prints some logs here, i can't paste here]
finished analyzing all records
i am in the promise
npm ERR! Test failed.  See above for more details.

I am expecting output in the following sequence:

[process business logic and prints some logs here, I can't paste here]
finished analyzing all records
AC 1: This is suite one
    I am in the before
       I am in the promise
        1) Test1 passed
        2) Test2 paseed
pk786
  • 2,140
  • 3
  • 18
  • 24
  • Why are you doing process.exit in your before? You are exiting the whole test suite as soon as your promise actually resolves. Not that it matters in this case, since you are *erroneously* running the `it` tests *before* that `Promise` actually resolves. – nicholaswmin Nov 30 '18 at 18:11

3 Answers3

2

You need to call done within your then & after you actually assigned responseData = data:

before((done) => {
  promiseResp.then((data) => {
    responseData = data;
    // Promise has resolved. Calling `done` to proceed to the `it` tests.
    done();
  })
  .catch((err) => {
    // Calling `done` with a truthy `err` argument, in case
    // the promise fails/rejects, to fail-early the test suite.
    done(err);
  })
})

otherwise before ends prematurely and proceeds to the next tests, before the promise actually resolves and assigns your responseData variable.

Here's a working example using the before hook:

const expect = require('chai').expect

const getFooValue = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('foo')
    }, 500)
  })
}

describe('#getFooValue()', function () {
  let responseData

  before(done => {
    getFooValue().then(data => {
      responseData = data
      done()
    })
    .catch(err => {
      done(err)
    })
  })

  it('response has a value of foo', () => {
    expect(responseData).to.equal('foo');
  })

  it('response is a String', () => {
    expect(responseData).to.be.a('String');
  })
})

What you're doing now is:

  • You define the Promise.
  • You (prematurely) call done and Mocha proceeds to execute the it tests.
  • The it tests run while responseData is still undefined.
  • The Promise within before eventually resolves and assigns the responseData variable.

...but at that point it's too late. The tests have already run.

nicholaswmin
  • 21,686
  • 15
  • 91
  • 167
  • i also had to had --timeout explicitly "test":"mocha **/mockExecution.js --timeout 4000" – pk786 Nov 30 '18 at 18:51
  • @pk786 Probably because your `Promise` resolves slower than Mocha's default 2000ms timeout. Also FYI although my answer pinpoints the issue with your code and works fine, estus's answer above is more correct than mine. – nicholaswmin Nov 30 '18 at 19:10
2

The use of done together with promises is an antipattern because this often results in incorrect control flow, like in this case. All major testing frameworks already support promises, including Mocha.

If default timeout (2 seconds) is not big enough for a promise to resolve, timeout value should be increased, e.g. as explained in this answer by setting it for current test suite (this in describe context). Notice that arrow function should be replaced with regular function to reach suite context.

It should be:

describe('AC 1: This is suite one', function () {
    this.timeout(60000);

    before(() => {
        return promiseResp.then((data) => {
            responseData = data;
        });
    });

    it('Test1', () => {            
       expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
    });
    ...

No need for catch for a promise; promise rejections will be handled by the framework. No need for done in tests; they are synchronous.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Darn that's exactly what I do in my tests, nevertheless I went ahead and answered with a less than ideal solution. Go figure. +1. – nicholaswmin Nov 30 '18 at 18:30
  • gives me error: 1) "before all" hook 0 passing (2s) 1 failing 1) "before all" hook: Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. – pk786 Nov 30 '18 at 18:45
  • 1
    @pk786 As you already pointed in another comment, the problem is that timeout is not big enough. I updated the answer for clarity. – Estus Flask Nov 30 '18 at 19:29
  • @estus Where do you see a missing `catch`? Can you comment on my answer? – nicholaswmin Nov 30 '18 at 19:50
  • @NicholasKyriakides Sorry, I confused your answer with another one that appeared in the same place on page reload. `catch` is ok in yours. Upvoted it, it clearly explains why `done` doesn't work as expected. – Estus Flask Nov 30 '18 at 20:04
1

There's no promise in your it(), so no reason for done(), but it should be called inside then() as it is a callback.

And overall it's cleaner to use async/await. It doesn't work well in before() though.

Also 'function()' is preferable in describe() to set timeout for the tests (Invoking it as a chained method never worked on my experience)

describe('AC 1: This is suite one', function() {
  this.timeout(12000); //12 sec timeout for each it()
  before((done) => {
     promiseResp().then((data) => {
         responseData = data;
         done();
     })      
  })

  it('Test1', () => {
      expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
  });

  it('Test2', () => {
      expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
  });

});
  • I added comment in package.json file liek this: test":"mocha **/mockExecution.js --timeout 4000" – pk786 Nov 30 '18 at 18:52
  • @pk786 I wonder if passing it as a command argument sets the timeout for it()'s separately or for the whole file to run? Could you please clarify? – Daniel Serendipity Nov 30 '18 at 18:57
  • 1
    @DanielSerendipity Passing timeout as a command line argument sets the timeout for all tests in that file. – nicholaswmin Nov 30 '18 at 19:18