Vitest: All 'describe' logic runs prior to all 'it'

The logic within the describe blocks is executed BEFORE any of the IT blocks are touched.

Think of describe logic as declaration that that gets processed before any of the IT implementation logic runs. Typically putting logic directly into describe blocks in vitest is NOT recommended.

GT-Sandbox-Snapshot

Code

import { describe, it, expect, test } from 'vitest';
 
describe('describe-level-1', () => {
  console.log("STDOUT: describe-level-1 pre it level-1");
 
  it('it-level-1', () => {
    console.log("STDOUT: it-level-1");
  });
 
  console.log("STDOUT: describe-level-1 post it level-1");
 
  describe('describe-level-2a', () => {
    console.log("STDOUT: describe-level-2a");
 
    it('it-level-2a', () => {
      console.log("STDOUT: it-level-2a");
    });
 
    describe('describe-level-3', () => {
      console.log("STDOUT: describe-level-3");
 
      it('it-level-3 1', () => {
        console.log("STDOUT: it-level-3 #1");
      });
 
      it('test-level-3 2', () => {
        console.log("STDOUT: it-level-3 #2");
      });
    });
  });
 
  describe('describe-level-2b', () => {
    console.log("STDOUT: describe-level-2b");
 
    it('it-level-2b', () => {
      console.log("STDOUT: it-level-2b");
    });
  });
});

Command to reproduce:

gt.sandbox.checkout.commit e18ef826fc7c3038372d \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./run.sh"

Recorded output of command:

> vitest run
 
stdout | unknown test
STDOUT: describe-level-1 pre it level-1
STDOUT: describe-level-1 post it level-1
STDOUT: describe-level-2a
STDOUT: describe-level-3
STDOUT: describe-level-2b
 
stdout | src/main.test.ts > describe-level-1 > it-level-1
STDOUT: it-level-1
 
stdout | src/main.test.ts > describe-level-1 > describe-level-2a > it-level-2a
STDOUT: it-level-2a
 
 ✓ src/main.test.ts  (5 tests) 2ms
stdout | src/main.test.ts > describe-level-1 > describe-level-2a > describe-level-3 > it-level-3 1
STDOUT: it-level-3 #1
 
stdout | src/main.test.ts > describe-level-1 > describe-level-2a > describe-level-3 > test-level-3 2
STDOUT: it-level-3 #2
 
stdout | src/main.test.ts > describe-level-1 > describe-level-2b > it-level-2b
STDOUT: it-level-2b
 
 
 Test Files  1 passed (1)
      Tests  5 passed (5)
   Start at  15:38:08
   Duration  512ms (transform 26ms, setup 0ms, collect 10ms, tests 2ms, environment 0ms, prepare 47ms)

Instead of plain desribe: describe+beforeAll

Use describe+beforeAll if you want GWT with vitest/describe spec.

Link to original

Vitest: describe and beforeAll to power GIVEN/WHEN/THEN

With vitest we cannot put logic inside describe for GIVEN/WHEN/THEN setup as with-describe

However, we can use beforeAll to power GIVEN/WHEN/THEN:

GT-Sandbox-Snapshot

Code

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
 
/**
 * NOTE: There is a quirk in Vitest where console.log() statements inside beforeAll()
 * hooks get buffered separately and appear under "unknown test" in the output, making
 * them appear out of order even though execution happens correctly. This executionLog
 * array tracks the actual execution order and prints it at the end via afterAll() to
 * prove the GIVEN/WHEN/THEN flow executes as expected.
 */
const executionLog: string[] = [];
 
describe('GIVEN a user account', () => {
  let account: any;
 
  beforeAll(() => {
    executionLog.push("1) GIVEN - Setting up account context");
    account = {balance: 0, transactions: []};
  });
 
  describe('WHEN depositing money', () => {
    beforeAll(() => {
      executionLog.push("2) WHEN - Depositing $100");
      account.balance += 100;
      account.transactions.push({type: 'deposit', amount: 100});
    });
 
    it('THEN balance should increase', () => {
      executionLog.push("3) THEN - Checking balance");
      expect(account.balance).toBe(100);
    });
 
    it('THEN transaction should be recorded', () => {
      executionLog.push("4) THEN - Checking transaction");
      expect(account.transactions).toHaveLength(1);
      expect(account.transactions[0].type).toBe('deposit');
    });
 
    describe('WHEN withdrawing money', () => {
      beforeAll(() => {
        executionLog.push("5) WHEN - Withdrawing $30");
        account.balance -= 30;
        account.transactions.push({type: 'withdrawal', amount: 30});
      });
 
      it('THEN balance should reflect withdrawal', () => {
        executionLog.push("6) THEN - Checking new balance");
        expect(account.balance).toBe(70);
      });
 
      it('THEN both transactions should be recorded', () => {
        executionLog.push("7) THEN - Checking all transactions");
        expect(account.transactions).toHaveLength(2);
      });
    });
  });
 
  describe('WHEN checking balance without deposits', () => {
    let cleanAccount: any;
 
    beforeAll(() => {
      executionLog.push("8) WHEN - Creating clean account");
      cleanAccount = {balance: 0, transactions: []};
    });
 
    it('THEN balance should be zero', () => {
      executionLog.push("9) THEN - Checking zero balance");
      expect(cleanAccount.balance).toBe(0);
    });
  });
 
  afterAll(() => {
    console.log("\n=== EXECUTION ORDER ===");
    executionLog.forEach(log => console.log(log));
    console.log("======================\n");
  });
});

Command to reproduce:

gt.sandbox.checkout.commit 2f99590ccd576a626a1c \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./run.sh"

Recorded output of command:

 
up to date, audited 182 packages in 614ms
 
33 packages are looking for funding
  run `npm fund` for details
 
2 moderate severity vulnerabilities
 
To address all issues (including breaking changes), run:
  npm audit fix --force
 
Run `npm audit` for details.
 
> glassthought-sandbox@1.0.0 test
> vitest run
 
 
 RUN  v0.34.6 /home/nickolaykondratyev/git_repos/glassthought-sandbox
 
 ✓ src/main.test.ts  (5 tests) 3ms
stdout | unknown test
 
=== EXECUTION ORDER ===
1) GIVEN - Setting up account context
2) WHEN - Depositing $100
3) THEN - Checking balance
4) THEN - Checking transaction
5) WHEN - Withdrawing $30
6) THEN - Checking new balance
7) THEN - Checking all transactions
8) WHEN - Creating clean account
9) THEN - Checking zero balance
======================
 
 
 
 Test Files  1 passed (1)
      Tests  5 passed (5)
   Start at  11:39:40
   Duration  463ms (transform 33ms, setup 0ms, collect 16ms, tests 3ms, environment 0ms, prepare 79ms)

Notes

  • it blocks that are in the outer level THEN describe.beforeAll will have priority in order of execution.
  • describe.beforeAll will execute once for that describe block.
  • Any code that you would like to put directly into describe block likely belongs in describe.beforeAll to act as you would expect (See with-describe)
Link to original