Jest’s global setup and teardown hooks are your secret weapon for managing the state of your integration tests, ensuring a clean slate before and after your entire test suite runs.

Let’s see this in action. Imagine we have a simple Express API that we want to test. We need to start this server before any tests run and shut it down afterward.

// server.js
const express = require('express');
const app = express();
const PORT = 3000;

app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Alice' }]);
});

const server = app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});

module.exports = server;

Now, let’s configure Jest to manage this server.

In your jest.config.js (or jest.config.ts), you’ll define globalSetup and globalTeardown:

// jest.config.js
module.exports = {
  // ... other Jest configurations
  globalSetup: '<rootDir>/jest.global-setup.js',
  globalTeardown: '<rootDir>/jest.global-teardown.js',
};

The globalSetup file will be responsible for starting our server. We’ll also store the server instance in a global variable so globalTeardown can access it.

// jest.global-setup.js
const server = require('./server'); // Assuming server.js exports the server instance

module.exports = async () => {
  console.log('Global setup: Starting server...');
  // In a real scenario, you might start a database, spin up Docker containers, etc.
  global.httpServer = server.listen(3000, () => {
    console.log('Server started for integration tests');
  });
  console.log('Global setup: Server started.');
};

And the globalTeardown file will shut it down.

// jest.global-teardown.js
module.exports = async () => {
  console.log('Global teardown: Shutting down server...');
  await new Promise((resolve) => {
    global.httpServer.close(() => {
      console.log('Server shut down.');
      resolve();
    });
  });
  console.log('Global teardown: Server shut down.');
};

Now, any test file in your project will have the server running before it executes, and it will be shut down only after all tests have completed.

This pattern is crucial for integration tests because it allows you to set up a consistent, isolated environment that mimics production as closely as possible. Instead of starting and stopping a server (or database, or message queue) for every single test file, you do it once for the entire suite. This dramatically speeds up your test runs and prevents tests from interfering with each other due to shared, mutable state. You can also use these hooks to seed databases with common test data, spin up external services using Docker Compose, or perform any other prerequisite setup that your integration tests rely on.

The most surprising bit is how globalSetup and globalTeardown are executed. Jest runs these scripts outside of the typical test runner context. This means they are executed as standalone Node.js scripts. Any require calls or top-level awaits within these files will be processed before any of your actual test files are even discovered. This isolation is what makes them so powerful for managing external dependencies or long-lived processes that your tests interact with.

The next logical step is to explore how to manage more complex environments, such as multiple services or databases, using these global hooks.

Want structured learning?

Take the full Jest course →