Jest Recap: What Runs When?

htho

Hauke T.

Posted on July 18, 2024

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.

💖 💪 🙅 🚩
htho
Hauke T.

Posted on July 18, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Jest Recap: What Runs When?
jest Jest Recap: What Runs When?

July 18, 2024