JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeTopicstestingTest Lifecycle Hooks
PrevNext
testing
beginner
8 min read

Test Lifecycle Hooks

afterAll
afterEach
beforeAll
beforeEach
jest
lifecycle
setup
teardown
test-pollution
vitest

Using beforeAll, beforeEach, afterEach, and afterAll hooks for test setup and teardown, understanding execution order, scope with describe blocks, and preventing test pollution.

Key Points

1Four Lifecycle Hooks

beforeAll runs once before all tests, beforeEach before each test, afterEach after each test, and afterAll once after all tests. All support async operations.

2Scoping with describe

Hooks are scoped to their describe block. Outer hooks run before inner hooks, allowing layered setup from general to specific state.

3Test Pollution Prevention

Use beforeEach for fresh setup and afterEach for cleanup to prevent one test's side effects from affecting others. Clear mocks, DOM, and global state consistently.

4beforeAll vs beforeEach

Use beforeAll for expensive shared setup (database connections, server startup). Use beforeEach for state that must be fresh per test (mock resets, test data creation).

What You'll Learn

  • Explain the execution order of all four lifecycle hooks with nested describe blocks
  • Choose between beforeAll and beforeEach based on whether state can be shared safely
  • Prevent test pollution through proper cleanup in afterEach hooks
  • Recognize when inline setup is preferable to lifecycle hooks for readability

Deep Dive

Test lifecycle hooks control setup and cleanup operations that run at specific points during test execution. Proper use of these hooks prevents test pollution (where one test's state affects another) and keeps test code DRY. Both Jest and Vitest use the same API for lifecycle hooks.

The Four Lifecycle Hooks

beforeAll(fn): Runs once before all tests in the current describe block (or file if not inside a describe). Use for expensive setup that can be shared across tests:

  • Database connections
  • Server startup
  • Loading large fixtures
  • MSW server initialization

beforeEach(fn): Runs before each individual test. Use for setup that must be fresh for every test:

  • Resetting mock state
  • Creating test data
  • Rendering components
  • Setting up DOM elements

afterEach(fn): Runs after each individual test. Use for cleanup that prevents test pollution:

  • Clearing mocks and spies
  • Cleaning up DOM
  • Resetting global state
  • Resetting MSW handlers

afterAll(fn): Runs once after all tests in the block complete. Use for expensive teardown:

  • Closing database connections
  • Stopping servers
  • Cleaning up temporary files
  • Closing MSW server

Execution Order

Understanding the execution order is critical for interviews:

JavaScript
beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('6 - afterAll'));
beforeEach(() => console.log('2 - beforeEach'));
afterEach(() => console.log('4 - afterEach'));
 
test('first test', () => console.log('3 - test'));
// Output: 1, 2, 3, 4, 2, 3, 4, 6 (for two tests)

Scoping with describe Blocks

Lifecycle hooks are scoped to their describe block. Outer hooks run before inner hooks:

JavaScript
describe('outer', () => {
  beforeEach(() => console.log('outer beforeEach'));
 
  describe('inner', () => {
    beforeEach(() => console.log('inner beforeEach'));
    test('example', () => {});
    // Runs: outer beforeEach -> inner beforeEach -> test
  });
});

This scoping lets you set up common state at the outer level and specific state at the inner level.

Preventing Test Pollution

Test pollution is when one test's side effects affect another test's behavior, causing intermittent failures that are difficult to debug. Common sources:

  • Shared mutable objects modified in tests
  • Mock functions not cleared between tests
  • DOM elements not removed after rendering
  • Global state (window.location, localStorage) not reset

The solution is consistent use of beforeEach for fresh setup and afterEach for cleanup:

JavaScript
beforeEach(() => {
  jest.clearAllMocks(); // or vi.clearAllMocks()
  localStorage.clear();
});
 
afterEach(() => {
  cleanup(); // RTL's cleanup (automatic in most setups)
});

Async Lifecycle Hooks

All lifecycle hooks support async operations. Return a promise or use async/await:

JavaScript
beforeAll(async () => {
  await database.connect();
});
 
afterAll(async () => {
  await database.disconnect();
});

Common Pitfalls

  • Putting test-specific setup in beforeAll instead of beforeEach, causing shared mutable state
  • Forgetting to clean up in afterEach, leading to flaky tests
  • Over-using lifecycle hooks for simple tests (inline setup is often clearer for simple cases)
  • Using beforeAll for mock setup that should reset between tests

Best Practice

Prefer inline setup for simple tests -- it makes tests self-contained and readable. Use lifecycle hooks when setup is expensive or when the same setup is repeated across many tests in a describe block.

Fun Fact

The beforeAll/afterAll pattern in testing was borrowed from database transactions. In database testing, you open a transaction in beforeAll and roll it back in afterAll, making the entire test suite run against a consistent database snapshot without persisting any changes.

Learn These First

Unit Testing Fundamentals

beginner

Test Organization and Structure

beginner

Continue Learning

Mocking Functions, Modules, and APIs

intermediate

Matchers and Assertions

beginner

Practice What You Learned

How do you use beforeEach, afterEach, beforeAll, and afterAll in Jest?
junior
setup
These are Jest lifecycle hooks for test setup and cleanup. beforeEach/afterEach run before/after each test. beforeAll/afterAll run once before/after all tests in a describe block. Use them to set up test data, mock functions, or clean up resources.
Previous
React Testing Library
Next
Testing Strategy for Large Applications
PrevNext