Fix: Jest Timeout — Exceeded timeout of 5000ms for a test
Quick Answer
How to fix Jest 'Exceeded timeout of 5000ms for a test' errors caused by unresolved promises, missing done callbacks, async/await mistakes, and slow database or network calls in tests.
The Error
You run Jest tests and one or more fail with:
Thrown: "Exceeded timeout of 5000ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test;
see https://jestjs.io/docs/api#testname-fn-timeout."Or:
● fetchUser › returns user data
Exceeded timeout of 5000ms for a test.
at node_modules/jest-jasmine2/build/queueRunner.js:47:12The test starts, runs for 5 seconds (Jest’s default timeout), then fails — even if the code itself would eventually succeed given more time.
Why This Happens
Jest uses a 5000ms (5 second) default timeout per test. If a test does not complete within that window, Jest forcibly fails it. Common causes:
- A Promise never resolves or rejects — a missing
resolve()/reject()call, or a callback that never fires. donecallback never called — using the callback-style async pattern but forgetting to calldone().awaiton a function that never settles — an infinite loop, deadlock, or event that never fires.- Actual network or database calls in tests without mocking — real I/O is slow and flaky.
beforeAllorbeforeEachtiming out — setup hooks count against the timeout too.- Missing
returnon a promise inside a test — Jest does not know to wait for it.
Fix 1: Return or Await the Promise
The most common mistake: the test completes synchronously, then the promise settles after Jest has already moved on — or Jest doesn’t wait at all.
Broken — promise not returned:
test("fetches user data", () => {
fetchUser(1).then(user => {
expect(user.name).toBe("Alice"); // Jest doesn't wait for this
});
// Test completes immediately — no assertion actually runs
});Fixed — return the promise:
test("fetches user data", () => {
return fetchUser(1).then(user => {
expect(user.name).toBe("Alice");
});
});Fixed — use async/await:
test("fetches user data", async () => {
const user = await fetchUser(1);
expect(user.name).toBe("Alice");
});async/await is the clearest pattern. Jest detects that the test function is async and waits for it to resolve before marking the test as done.
Fix 2: Fix the done Callback Pattern
When using the done callback (older pattern), Jest waits until done() is called. If it is never called — due to an error or a missing branch — the test times out.
Broken — done not called on error:
test("sends notification", (done) => {
sendNotification("[email protected]", (err, result) => {
if (err) {
// Forgot to call done() or done(err)
console.error(err);
return;
}
expect(result.sent).toBe(true);
done();
});
});Fixed — always call done:
test("sends notification", (done) => {
sendNotification("[email protected]", (err, result) => {
if (err) {
done(err); // Pass the error to done — fails the test with the actual error
return;
}
expect(result.sent).toBe(true);
done();
});
});Calling done(err) with an error argument fails the test immediately with a descriptive error instead of timing out.
Pro Tip: Prefer
async/awaitover thedonecallback pattern. It is easier to read, less error-prone, and avoids the “forgot to call done” class of bugs entirely. Only usedonewhen working with callback-based APIs that cannot be promisified.
Fix 3: Mock Network and Database Calls
Tests that make real HTTP requests or database queries are slow, flaky, and environment-dependent. Mock them instead:
Broken — real HTTP call:
test("loads posts from API", async () => {
const posts = await fetch("https://jsonplaceholder.typicode.com/posts")
.then(r => r.json());
expect(posts.length).toBeGreaterThan(0);
// Fails if the network is slow, the API is down, or the test runs in CI
});Fixed — mock with jest.fn() or MSW:
// Using jest.spyOn to mock fetch
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => [{ id: 1, title: "Post 1" }, { id: 2, title: "Post 2" }],
});
test("loads posts from API", async () => {
const posts = await loadPosts();
expect(posts.length).toBe(2);
expect(global.fetch).toHaveBeenCalledWith("/api/posts");
});Using jest.mock() for modules:
jest.mock("./api", () => ({
fetchUser: jest.fn().mockResolvedValue({ id: 1, name: "Alice" }),
}));
import { fetchUser } from "./api";
test("fetchUser returns user", async () => {
const user = await fetchUser(1);
expect(user.name).toBe("Alice");
});For database calls, use an in-memory database (SQLite with :memory:, mongodb-memory-server for MongoDB) or mock the database layer entirely.
Fix 4: Increase the Timeout for Slow Tests
If the test is legitimately slow (integration test, large file processing, real database), increase the timeout:
Per-test timeout:
test("processes large CSV file", async () => {
const result = await processCSV("large-file.csv");
expect(result.rows).toBe(100000);
}, 30000); // 30 second timeout for this test onlyPer-suite timeout in beforeAll:
describe("database integration tests", () => {
beforeAll(async () => {
await setupDatabase();
}, 60000); // 60 seconds for setup
test("queries users table", async () => {
const users = await db.query("SELECT * FROM users");
expect(users.length).toBeGreaterThan(0);
}, 15000); // 15 seconds per test
});Global timeout for all tests:
In jest.config.js:
module.exports = {
testTimeout: 15000, // 15 seconds for all tests
};Or in jest.config.ts:
import type { Config } from "jest";
const config: Config = {
testTimeout: 15000,
};
export default config;Common Mistake: Setting a very high global timeout (e.g., 60000ms) to “fix” timeouts hides real problems. A test that times out at 5 seconds would just time out at 60 seconds instead. Increase timeout only for tests that are genuinely slow by design, and fix the underlying issue (missing mock, unresolved promise) for everything else.
Fix 5: Fix beforeAll and afterAll Timeouts
Setup and teardown hooks have their own timeout (same as the test timeout by default). Database connections and server startup can be slow:
Broken — beforeAll times out:
beforeAll(async () => {
await startServer(); // Takes 8 seconds — times out at 5
await seedDatabase();
});Fixed — pass timeout as second argument:
beforeAll(async () => {
await startServer();
await seedDatabase();
}, 30000); // 30 seconds for setup
afterAll(async () => {
await stopServer();
await clearDatabase();
}, 15000);Fix 6: Fix Promises That Never Settle
If a promise hangs indefinitely, no timeout increase will fix it — find why it never resolves:
Common causes:
// Broken — resolve/reject never called
const wait = () => new Promise((resolve, reject) => {
someEmitter.on("done", () => {
// resolve() is missing — promise hangs forever
console.log("done");
});
});
// Broken — awaiting a non-promise
async function test() {
await undefined; // Immediately resolves, but if you await a never-resolving thing:
await new Promise(() => {}); // Hangs forever
}Diagnose with a race and timeout:
const withTimeout = (promise, ms) =>
Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Timed out after ${ms}ms`)), ms)
),
]);
test("operation completes", async () => {
const result = await withTimeout(myOperation(), 3000);
expect(result).toBe("done");
});This gives you a descriptive error (“Timed out after 3000ms”) instead of Jest’s generic timeout message, making it easier to see which operation hangs.
Fix 7: Fix Open Handles Warning
After fixing timeouts, Jest may warn:
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests.
Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.Open handles (server sockets, database connections, timers) prevent Jest from exiting cleanly. Find them:
npx jest --detectOpenHandlesThen close them in afterAll:
let server;
beforeAll(async () => {
server = app.listen(3001);
});
afterAll(async () => {
await new Promise(resolve => server.close(resolve)); // Close the server
await db.end(); // Close database connections
});For timer-based open handles, use Jest’s fake timers:
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});Still Not Working?
Check for circular async dependencies. If function A awaits function B, and B awaits A, both hang indefinitely. Add logging inside each function to trace where execution stops.
Check event listeners that never fire. If a promise resolves only when an event is emitted, and that event never fires (e.g., a stream that never closes), the promise hangs. Add a timeout or verify the event source is working.
Run the specific failing test in isolation:
npx jest --testPathPattern="your-test-file" --verboseIsolating the test removes interference from other tests and makes the hang easier to reproduce.
Check for missing jest.config.js or package.json jest config. If Jest is picking up the wrong configuration, the testTimeout setting may not apply. Run npx jest --showConfig to see the resolved configuration.
For module import errors that prevent tests from running at all, see Fix: Jest Cannot Find Module.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: jest.mock() Not Working — Module Not Being Replaced in Tests
How to fix jest.mock() not intercepting module calls — why mocks are ignored, how to correctly mock ES modules, default exports, named exports, and fix hoisting issues in Jest tests.
Fix: UnhandledPromiseRejectionWarning / UnhandledPromiseRejection
How to fix UnhandledPromiseRejectionWarning in Node.js and unhandled promise rejection errors in JavaScript caused by missing catch handlers, async/await mistakes, and event emitter errors.
Fix: Jest Cannot Find Module Error
How to fix the Jest 'Cannot find module' error by configuring moduleNameMapper, moduleDirectories, tsconfig paths, and resolving ESM, monorepo, and dependency issues.
Fix: Express req.body Is undefined
How to fix req.body being undefined in Express — missing body-parser middleware, wrong Content-Type header, middleware order issues, and multipart form data handling.