Skip to main content

How to Resolve the "JavaScript heap out of memory" Error in Jest in JavaScript

The "JavaScript heap out of memory" or "Your test suite is leaking memory" error in Jest is a sign that your tests are consuming more memory than Node.js allows. This typically happens when tests create resources (like timers, event listeners, or server connections) but fail to clean them up afterward, causing memory usage to grow with each test file until the process crashes.

This guide will walk you through a structured approach to solving this error, starting with diagnostics to confirm the leak, then covering the common causes and their proper solutions, and finally discussing workarounds for resource-constrained environments.

What is a Memory Leak in a Test?

An ideal test is isolated: it sets itself up, runs its assertions, and cleans up completely, leaving no trace. A memory leak occurs when a test leaves behind "garbage" that the JavaScript garbage collector cannot reclaim. This garbage could be:

  • A setInterval that is still running.
  • An open database or server connection.
  • An event listener attached to the document or window.

When Jest runs the next test file, the garbage from the previous one is still in memory. This causes the heap size to grow until it hits the limit and crashes.

Step 1: Diagnosing the Leak with --logHeapUsage

Before you start changing code, confirm that you have a memory leak. The --logHeapUsage flag tells Jest to print the heap size after each test.

Command:

npx jest --logHeapUsage

Output: if you see the heap size consistently increasing from one test to the next, you have confirmed a memory leak.

PASS  __tests__/file1.test.js (156 MB heap size)
PASS __tests__/file2.test.js (196 MB heap size)
PASS __tests__/file3.test.js (201 MB heap size)
PASS __tests__/file4.test.js (208 MB heap size) <-- Memory is growing!

Step 2: Common Causes and How to Fix Them

The best way to solve a memory leak is to fix it in your test code by using Jest's cleanup hooks (afterEach or afterAll).

###. Unmocked Timers (setInterval, setTimeout) Timers that are not cleared will keep running forever, preventing cleanup.

Solution: Use Jest's fake timers and ensure they are reset.

// At the top of your test file
jest.useFakeTimers();

// In an afterEach hook to ensure cleanup
afterEach(() => {
jest.clearAllTimers();
});

Open Handles (Servers, Sockets, DB Connections)

If a test starts a server or opens a database connection, it must close it.

Solution: Use the afterAll or afterEach hooks to close the handle.

let server;

beforeAll(() => {
server = require('./my-server').listen(3000);
});

afterAll(done => {
server.close(done); // Close the server and call `done` when finished.
});

DOM Event Listeners Not Being Removed

Listeners attached to global objects like document or window will persist if not explicitly removed.

Solution: Remove the listener in a cleanup function. Frameworks like React Testing Library do this automatically with their cleanup function.

afterEach(() => {
// If you manually added a listener in your test, remove it here.
document.body.removeEventListener('click', myFunction);
});

Step 3: Workarounds for Managing Memory

If you cannot immediately fix the leak, or if you are in a resource-constrained environment like a CI server, these flags can help manage memory usage.

Forcing Garbage Collection (--expose-gc)

This flag gives your tests access to a global.gc() function to manually trigger garbage collection. It's often combined with --runInBand.

# In package.json scripts:
"test": "node --expose-gc ./node_modules/.bin/jest --runInBand"

You can then call global.gc() in an afterEach hook. This is a workaround, not a true fix for a leak.

Running Tests Serially (--runInBand and --maxWorkers)

Jest runs tests in parallel using "workers." This can consume a lot of memory. Forcing tests to run one at a time reduces the peak memory usage.

# Run all tests in a single thread
npx jest --runInBand

# A more modern equivalent
npx jest --maxWorkers=1

Recycling Workers (workerIdleMemoryLimit)

You can configure Jest to automatically recycle a worker process if its memory usage exceeds a certain limit.

jest.config.js:

module.exports = {
// Recycle a worker if it uses more than 512MB
workerIdleMemoryLimit: '512MB',
};

Step 4: Environmental Fixes (Last Resort)

If all else fails, the issue might be related to the specific combination of your Node.js and Jest versions.

  • Update Jest: Ensure you are on the latest version of jest and related packages, as memory management improves over time.
    npm install jest@latest jest-environment-jsdom@latest --save-dev
  • Change Node.js Version: Some Jest memory issues have been historically tied to specific Node.js versions. As a last resort, consider using a Node Version Manager (nvm or nvs) to switch to a different major LTS version (e.g., Node 18 or 16), as this has been known to resolve certain deep-seated garbage collection bugs.

Conclusion

The "heap out of memory" error is a signal that your tests are not properly isolated.

  • First, diagnose the problem using --logHeapUsage to confirm that memory is growing.
  • The best solution is to fix the root cause by ensuring all resources (timers, servers, listeners) are cleaned up in afterEach or afterAll hooks.
  • If you need a temporary solution or are in a low-memory environment, use workaround flags like --runInBand or --maxWorkers=1.

By focusing on proper test cleanup, you can eliminate memory leaks and create a faster, more reliable test suite.