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.

Image of Docusign

Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs