I would like to understand why all the blocks describe()
run before the it()
in inside of each describe()
.
Here's a CodeSandbox example with a log per each describe
and it
block.
describe("sum A", () => {
window.innerWidth = 100;
console.log("describe A", window.innerWidth);
beforeAll(() => {
window.innerWidth = 105;
console.log("describe A before", window.innerWidth);
});
it("1. should add 1 and 2", () => {
console.log("it A1", window.innerWidth);
expect(1 + 2).toBe(3);
});
});
describe("sum B", () => {
console.log("describe B", window.innerWidth, "why does it run before it A1?");
beforeAll(() => {
window.innerWidth = 205;
console.log("describe B before", window.innerWidth);
});
it("1. should add 1 and 2", () => {
console.log("it B1", window.innerWidth);
expect(1 + 2).toBe(3);
});
});
You'll notice that the log from the second describe block runs before the it()
inside the first describe block.
Why does that happen? Should we avoid doing stuff in that part of the code and prefer using beforeAll()
when we need to share and scope data in that describe block?
When Jest
runs it looks for all of the test files and runs each one.
Each test file runs within an environment provided by Jest
that includes globals like describe
, it
, beforeAll
, etc. All of these globals have a callback parameter that defines their behavior.
When a test file runs the top-level code runs...including any top-level describe
calls.
When a describe
runs it registers a test suite, and then its callback is called immediately.
This is different than it
, beforeAll
, beforeEach
, etc. where the callback is recorded but not immediately called.
This means that all describe
callback functions are called depth-first in the order they appear in a test file, as can be seen in this simple example:
describe('1', () => {
console.log('1');
describe('2', () => { console.log('2'); });
describe('3', () => {
console.log('3');
describe('4', () => { console.log('4'); })
describe('5', () => { console.log('5'); })
})
describe('6', () => { console.log('6'); })
})
describe('7', () => {
console.log('7');
it('(since there has to be at least one test)', () => { });
})
...which logs 1
- 7
in order.
This initial run of all the describe
callbacks is called the collection phase during which the test suites are defined and all of the callbacks for any beforeAll
, beforeEach
, it
, test
, etc. are collected.
After the collection phase completes, Jest
...
runs all the tests serially in the order they were encountered in the collection phase, waiting for each to finish and be tidied up before moving on.
For each test (each callback function registered with the global it
or test
functions) Jest
links together any before callbacks, the test callback itself, and any after callbacks and runs the resulting functions in order.
Should we avoid doing stuff in that part of the code and prefer using
beforeAll()
when we need to share and scope data in that describe block?
For simple stuff that isn't shared it's fine to have it in the describe
:
describe('val', () => {
const val = '1';
it('should be 1', () => {
expect(val).toBe('1'); // Success!
});
});
...but code in the describe
can cause issues with shared data:
describe('val', () => {
let val;
describe('1', () => {
val = '1';
it('should be 1', () => {
expect(val).toBe('1'); // FAIL! (val gets set to 2 in the second describe)
})
})
describe('2', () => {
val = '2';
it('should be 2', () => {
expect(val).toBe('2'); // Success!
})
})
});
...which can be fixed either by using before
calls:
describe('val', () => {
let val;
describe('1', () => {
beforeEach(() => {
val = '1';
});
it('should be 1', () => {
expect(val).toBe('1'); // Success!
})
})
describe('2', () => {
beforeEach(() => {
val = '2';
});
it('should be 2', () => {
expect(val).toBe('2'); // Success!
})
})
});
...or simply scoping the data to the describe
:
describe('val', () => {
describe('1', () => {
const val = '1';
it('should be 1', () => {
expect(val).toBe('1'); // Success!
})
})
describe('2', () => {
const val = '2';
it('should be 2', () => {
expect(val).toBe('2'); // Success!
})
})
});
In your example you are using window.innerWidth
which is a shared global, so you will want to use before
functions since it can't be scoped to the describe
.
Also note that you can't return anything from a describe
so if your tests require any asynchronous setup then you will need to use a before
function where you can return a Promise
for Jest
to await:
const somethingAsync = () => Promise.resolve('1');
describe('val', () => {
let val;
beforeAll(async () => {
val = await somethingAsync();
});
it('should be 1', () => {
expect(val).toBe('1'); // Success!
});
});
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