Better way to handle mochajs timeout - node.js

I am trying to implement mocha testing, however it doesn't seem to work the way I expect it to. My code looks like the following:
async function getFoo() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved')
}, 2500)
})
}
describe(`Testing stuff`, function () {
it('resolves with foo', () => {
return getFoo().then(result => {
assert.equal(result, 'foo')
})
})
})
Error message:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
To me it seems like I have to increase the threshold of the timeout.
Is there a better way to do this? Since I don't know how long each test might take. I am testing nodejs child processes
best regards

As it is described in the error you need to use done() end of your test.
function delay(timeout = 2500) {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved')
}, timeout)
})
}
describe(`Testing stuff`, function (done) {
it('resolves with foo', (done) => {
return delay().then(result => {
assert.equal(result, 'foo')
done()
})
})
});
// better readability
describe(`Testing stuff`, function (done) {
it('resolves with foo', (done) => {
const result = await delay();
assert.equal(result, 'foo')
done();
})
});
But as you described you are testing child_processes which I don't recommend. Because to test child_process you need to promisify the child_process which doesn't make sense. Check here for more info How to promisify Node's child_process.exec and child_process.execFile functions with Bluebird?
If you really want to test child_process maybe you can mock the process with the link i gave but only for testing purposes.

Related

Async beforeAll() does not finish before beforeEach() is called

In Jest, beforeAll() is supposed to run before beforeEach().
The problem is that when I use an async callback for beforeAll(), Jest doesn't wait for the callback to finish before going on to beforeEach().
How can I force Jest to wait for an async beforeAll() callback to finish before proceeding to beforeEach()?
Minimal reproducible example
tests/myTest.test.js
const connectToMongo = require('../my_async_callback')
// This uses an async callback.
beforeAll(connectToMongo)
// This is entered before the beforeAll block finishes. =,(
beforeEach(() => {
console.log('entered body of beforeEach')
})
test('t1'), () => {
expect(1).toBe(1)
}
test('t2'), () => {
expect(2+2).toBe(4)
}
test('t3'), () => {
expect(3+3+3).toBe(9)
}
my_async_callback.js
const connectToMongo = async () => {
try {
await mongoose.connect(config.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
})
console.log('Connected to MongoDB')
} catch (err) {
console.log(`Error connecting to MongoDB: ${err.message}`)
}
}
module.exports = connectToMongo
UPDATE: As the accepted answer helpfully points out, Jest actually does wait for beforeAll to finish first, except in the case of a broken Promise chain or a timeout. So, the premise of my question is false. My connectToMongo function was timing out, and simply increasing the Jest timeout solved the problem.
The problem is that when I use an async callback for beforeAll(), Jest doesn't wait for the callback to finish before going on to beforeEach().
How can I force Jest to wait for an async beforeAll() callback to finish before proceeding to beforeEach()?
TLDR
The short answer is that Jest does wait for an async beforeAll() callback to finish before proceeding to beforeEach().
This means that if beforeEach() is running before something that should run in beforeAll() then the Promise chain must be broken or the beforeAll function is timing out.
Queue Runner in Jest
All of the beforeAll, beforeEach, test, afterEach, afterAll functions associated with a test are collected in queueableFns and are chained on these lines in queueRunner.ts:
const result = options.queueableFns.reduce(
(promise, fn) => promise.then(() => mapper(fn)),
Promise.resolve(),
);
So Jest starts with a resolved Promise and chains every function, in order, to the Promise chain with .then.
This behavior can be seen with the following test:
const order = [];
// first beforeAll with async function
beforeAll(async () => {
order.push(1);
await new Promise((resolve) => { setTimeout(resolve, 1000); });
order.push(2);
});
// first beforeEach with done callback
beforeEach(done => {
order.push(4);
setTimeout(() => {
order.push(6);
done();
}, 1000);
order.push(5);
});
// second beforeEach
beforeEach(() => {
order.push(7);
});
// second beforeAll
beforeAll(() => {
order.push(3);
});
it("should run in order", () => {
expect(order).toEqual([1, 2, 3, 4, 5, 6, 7]); // SUCCESS!
});
Broken Promise Chain
If beforeEach is running before something that should run in beforeAll then it is possible the Promise chain is broken:
const order = [];
// does not return Promise and will break the Promise chain
const func = () => {
setTimeout(() => { order.push(2); }, 1000);
}
const asyncFunc = async () => {
order.push(1);
await func(); // doesn't actually wait for 2 to be pushed
order.push(3);
}
beforeAll(asyncFunc);
beforeEach(() => {
order.push(4);
});
it("should run in order", () => {
expect(order).toEqual([1, 2, 3, 4]); // FAIL: [1, 3, 4]
});
Timeout
...or there is a timeout (note that the timeout will be reported by Jest in the output):
const order = [];
jest.setTimeout(100); // 100ms timeout
const asyncFunc = async () => {
order.push(1);
await new Promise(resolve => { setTimeout(resolve, 1000); }); // times out
order.push(2);
}
beforeAll(asyncFunc);
beforeEach(() => {
order.push(3);
});
it("should run in order", () => {
expect(order).toEqual([1, 2, 3]); // FAIL: [1, 3] and Timeout error
});
If there is async function and callback you can call done. if you want to pass callback inside the async function you are free!
Let me show you;
beforeAll(async (done) => {
await connectToMongo().catch(done) // if there is error it finish with error payload
done(); // it says i am finish. also you can use it on your callback function to say i am done.
})
This happened to me when upgrading to angular 14. Solution was to upgrade zone.js to 0.11.8. Found solution here: https://github.com/angular/angular/issues/45476#issuecomment-1195153212
connectToMongo function is an async function, not a async callback (async function with a function as a parameter ???)
beforeEach will be called when beforeAll is finished, and it still works well.
beforeAll(connectToMongo) will be done right after you call it, this means, it will not wait until the db connect success.
Just wait until connectToMongo done and continue:
beforeAll(async () => { // async function
await connectToMongo() // wait until connected to db
})

How to do test after connection with mongoose to atlas

I want to test create user, so after connection to the DB I want to delete all the users that I tested and after it, I want to create new for the test.(Mocha)
test_helper.js
mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.connection
.once('open', () => {
console.log("connected")
})
.on('error', (error) => {
console.warn('Warning', error)
});
beforeEach((done) => {
mongoose.connection.collections.users.drop(() => {
done();
}
)
})
create_test.js
describe('Creating', () => {
it('saves a user', () => {
const testUser = new User({ name: 'Test' });
testUser.save();
});
});
I am getting the next error
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
what do I miss?
Some topics before my answer:
Asynchronous coding:
If you don't know/ are sure how it works, I recommend you to stop and learn about it: callback,Promises, async / await.
Testing:
Basically the flow is : create some scenario and assert some case, for example the attached code. I created a user and tested if it really work.
Testing Asynchronous code: after you read about callback function you can understand that done() is a callback function that permit finish the current async. function and pass to the next async. function.
testUser.save() returns a Promise and you aren't handling it.
...
testUser.save().then(()=>{
assert(testUser.isNew === false)
done();
}
...
It should work, but if you want to test some scenarios one after other you should handle it.
describe('Creating', () => {
it('some test', (done) => {
// some logic
done()
}
it('another test', (done) => {
// some logic
done()
}
});
});

How to use jest.each asynchronously

I am having problems loading filenames into jest.each asynchronously.
My code:
let files: string[][]
function getFilesWorking() {
files = [["test1"], ["test2"]]
}
async function getFilesAsync() {
files = await Promise.resolve([["test1"], ["test2"]])
}
beforeAll(() => {
console.log("before")
})
describe.only("Name of the group", () => {
getFilesAsync()
test.each(files)("runs", f => {})
})
beforeAll is executed before each test but NOT before initialization of test.each, so I end up with undefined.
How can I load files before using test.each?
You can pass an async callback to beforeAll and await getFilesAsync within it
beforeAll(async () => {
await getFilesAsync();
})
As of Jest 28.1.3 and prior, this is not possible. There is an open issue documenting this behavior.
The best thing you can do for now is put your tests in a regular it() test and do a deep value comparison:
it('tests an array of cases', async () => {
const data = await getSomeAsyncData()
const expectedData = [ ... ]
expect(data).toEqual(expectedData)
})
You can use beforeEach to set up code that will run prior to tests for any given scope, https://jestjs.io/docs/setup-teardown:
beforeEach(() => {
console.log('before every test');
});
describe.only(('Name of the group') => {
beforeEach(() => {
console.log('before tests in this describe block');
})
})
Jest is only going to run the tests in your describe.only block. If you want to use beforeEach in other blocks and run those tests as well, change describe.only to describe.
(Edit: I know this is a year late, I'm just trying to look for a similar problem/solution set and thought I could answer this.)

Mocha test cases executes before promise gets the data

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

How to use nockback with promises?

I am trying to use nockBack to automate my fixtures recording/playback with code that uses promises and chai-as-promised.
But I can't figure out how to make the async nockBack work with it.
context('sad path', () => {
it('should reject the promise', () => {
return nockBack('myfixture.json')
.then(() => assert.isRejected(myPromise.doIt(), Error));
});
});
This fails with a timeout because no callback is ever called.
I also tried with const nockBack = Promise.promisifyAll(require('nock').back), but it still times out.
Any thoughts on how to use nockBack with promises and chai-as-promised?

Resources