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()
?
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 tobeforeEach()
.How can I force Jest to wait for an async
beforeAll()
callback to finish before proceeding tobeforeEach()
?
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.
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!
});
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]
});
...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 you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With