DEV Community

Hauke T.
Hauke T.

Posted on

Jest Recap: What Runs When?

TL;DR: Execution Order

  1. Everything on the top-level and in describe() blocks (where describe() blocks are basically IIFEs)
  2. beforeAll()
    1. top-level
    2. 1st level describe()
    3. Nth level describe()
  3. beforeEach()
    1. top-level
    2. 1st level describe()
    3. Nth level describe()
  4. test()
  5. afterEach()
    1. Nth level describe()
    2. 1st level describe()
    3. top-level
  6. afterAll()
    1. Nth level describe()
    2. 1st level describe()
    3. top-level

Disclaimer

I assume you have a basic understanding of jest and unit-testing. I will not explain the meaning of the hooks. This is more of cheat-sheet/reference type of post.

There are Rules

At first jest seems to do stuff magically. What gets executed when? But if you think about it for a minute it gets obvious less confusing.

Maybe these simple "rules" help:

  1. Each file is executed independently: nothing you do in A.test.js affects B.test.js. (unless you start to access external resources)
  2. describe() callbacks are executed immediately.
  3. hooks (beforeAll/afterAll, beforeEach/afterEach) are executed from outer scope (top-level/module) to inner scope (describe).

Basic Example

console.log("./<start>"); beforeAll(() => { console.log("./beforeAll"); }) beforeEach(() => { console.log("./beforeEach"); }) afterAll(() => { console.log("./afterAll"); }) afterEach(() => { console.log("./afterEach"); }) describe("foo", () => { console.log("./describe(foo)/<start>"); beforeAll(() => { console.log("./describe(foo)/beforeAll"); }) beforeEach(() => { console.log("./describe(foo)/beforeEach"); }) afterAll(() => { console.log("./describe(foo)/afterAll"); }) afterEach(() => { console.log("./describe(foo)/afterEach"); }) test("testFoo", () => { console.log("./describe(foo)/test(testFoo)"); }) console.log("./describe(foo)/<end>"); }) describe("bar", () => { console.log("./describe(bar)/<start>"); beforeAll(() => { console.log("./describe(bar)/beforeAll"); }) beforeEach(() => { console.log("./describe(bar)/beforeEach"); }) afterAll(() => { console.log("./describe(bar)/afterAll"); }) afterEach(() => { console.log("./describe(bar)/afterEach"); }) test("testBar", () => { console.log("./describe(bar)/test(testBar)"); }) test("testOtherBar", () => { console.log("./describe(bar)/test(testOtherBar)"); }) console.log("./describe(bar)/<end>"); }) console.log("./<end>"); 
Enter fullscreen mode Exit fullscreen mode

This is the result (after I removed other output):

./<start> ./describe(foo)/<start> ./describe(foo)/<end> ./describe(bar)/<start> ./describe(bar)/<end> ./<end> ./beforeAll ./describe(foo)/beforeAll ./beforeEach ./describe(foo)/beforeEach ./describe(foo)/test(testFoo) ./describe(foo)/afterEach ./afterEach ./describe(foo)/afterAll ./describe(bar)/beforeAll ./beforeEach ./describe(bar)/beforeEach ./describe(bar)/test(testBar) ./describe(bar)/afterEach ./afterEach ./beforeEach ./describe(bar)/beforeEach ./describe(bar)/test(testOtherBar) ./describe(bar)/afterEach ./afterEach ./describe(bar)/afterAll ./afterAll 
Enter fullscreen mode Exit fullscreen mode

What happens?

Everything on the top-level and in describe callbacks is executed immediately:

./<start> ./describe(foo)/<start> ./describe(foo)/<end> ./describe(bar)/<start> ./describe(bar)/<end> ./<end> [...] 
Enter fullscreen mode Exit fullscreen mode

beforeAll and afterAll on the top-level are a "brace" around all tests. Each executed only once.

[...] ./beforeAll [...] ./afterAll 
Enter fullscreen mode Exit fullscreen mode

./describe(*)/beforeAll and ./describe(*)/afterAll are a brace around all tests in that describe callback. Each executed only once.

[...] ./describe(foo)/beforeAll [...] ./describe(foo)/afterAll ./describe(bar)/beforeAll [...] ./describe(bar)/afterAll [...] 
Enter fullscreen mode Exit fullscreen mode

beforeEach and afterEach are a braces around each test. The top-level is the outer brace. The describe level is the inner brace.

[...] ./beforeEach ./describe(*)/beforeEach [...] ./describe(*)/afterEach ./afterEach [...] 
Enter fullscreen mode Exit fullscreen mode

Advanced Version

This is an advanced example with a nested describe block. It produces XMLish results to emphasize the hierarchical nature of the execution steps.

console.log("<top-level>"); beforeAll(() => { console.log("<all>"); }) beforeEach(() => { console.log("<each>"); }) afterAll(() => { console.log("</all>"); }) afterEach(() => { console.log("</each>"); }) describe("foo", () => { console.log("<describe id=\"foo\">"); beforeAll(() => { console.log("<all in=\"foo\">"); }) beforeEach(() => { console.log("<each in=\"foo\">"); }) afterAll(() => { console.log("</all> <!-- in=\"foo\" -->"); }) afterEach(() => { console.log("</each> <!-- in=\"foo\" -->"); }) test("testFoo", () => { console.log("<test in=\"foo\" id=\"testFoo\" />"); }) console.log("</describe> <!-- id=\"foo\" -->"); }) describe("bar", () => { describe("barinner", () => { console.log("<describe id=\"barinner\">"); beforeAll(() => { console.log("<all in=\"barinner\">"); }) beforeEach(() => { console.log("<each in=\"barinner\">"); }) afterAll(() => { console.log("</all> <!-- in=\"barinner\" -->"); }) afterEach(() => { console.log("</each> <!-- in=\"barinner\" -->"); }) test("testBarInner", () => { console.log("<test in=\"barinner\" id=\"testBarInner\" />"); }) console.log("</describe> <!-- id=\"barinner\" -->"); }) console.log("<describe id=\"bar\">"); beforeAll(() => { console.log("<all in=\"bar\">"); }) beforeEach(() => { console.log("<each in=\"bar\">"); }) afterAll(() => { console.log("</all> <!-- in=\"bar\" -->"); }) afterEach(() => { console.log("</each> <!-- in=\"bar\" -->"); }) test("testBar", () => { console.log("<test in=\"bar\" id=\"testBar\" />"); }) test("testOtherBar", () => { console.log("<test in=\"bar\" id=\"testOtherBar\" />"); }) console.log("</describe> <!-- id=\"bar\" -->"); }) console.log("</top-level>"); 
Enter fullscreen mode Exit fullscreen mode

This is the output after some cleanup and formatting:

<top-level> <describe id="foo"> </describe> <!-- id="foo" --> <describe id="barinner"> </describe> <!-- id="barinner" --> <describe id="bar"> </describe> <!-- id="bar" --> </top-level> <all> <all in="foo"> <each> <each in="foo"> <test in="foo" id="testFoo" /> </each> <!-- in="foo" --> </each> </all> <!-- in="foo" --> <all in="bar"> <all in="barinner"> <each> <each in="bar"> <each in="barinner"> <test in="barinner" id="testBarInner" /> </each> <!-- in="barinner" --> </each> <!-- in="bar" --> </each> </all> <!-- in="barinner" --> <each> <each in="bar"> <test in="bar" id="testBar" /> </each> <!-- in="bar" --> </each> <each> <each in="bar"> <test in="bar" id="otherBar" /> </each> <!-- in="bar" --> </each> </all> <!-- in="bar" --> </all> 
Enter fullscreen mode Exit fullscreen mode

That's all there is to know about the execution order.

Top comments (0)