Q29 of 40 · JavaScript

How do you correctly test Promise-based code in Jest?

JavaScriptMidjavascriptjesttestingpromisesasyncassertions

Short answer

Short answer: Return or await the Promise in the test — otherwise Jest finishes before the assertion runs. Use `expect(promise).resolves.toBe()` for fulfillment, `expect(promise).rejects.toThrow()` for rejection. Always assert the number of expected assertions with `expect.assertions(n)` when testing rejections.

Detail

Testing async code correctly in Jest requires explicit patterns — Jest will mark a test as passed if it returns before any async assertion fires.

Always return or await: Jest considers a test done when its function returns. If you don't await or return a Promise, the test ends immediately — assertions that run later are ignored, giving a false pass.

resolves / rejects matchers: await expect(fetchUser(1)).resolves.toMatchObject({ id: 1 }) is cleaner than manually awaiting and asserting. await expect(badFetch()).rejects.toThrow('404') tests rejection.

expect.assertions(n): Declares the number of assertions that must run. If a rejection path is never reached (because the Promise resolves unexpectedly), the test fails rather than silently passing.

Testing async errors: Wrap in try/catch with expect.assertions, or use the rejects matcher — never rely on a test passing because no assertion ran.

Timeouts: Long-running async tests can be configured with Jest's jest.setTimeout(ms) or the test-level timeout option.

// EXAMPLE

// BAD — Jest finishes before assertion fires
test("bad async test", () => {
  fetchUser(1).then(u => {
    expect(u.name).toBe("Alice"); // runs after test is marked done!
  });
});

// GOOD — return the Promise
test("return promise", () => {
  return fetchUser(1).then(u => expect(u.name).toBe("Alice"));
});

// BETTER — await
test("await", async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe("Alice");
});

// BEST for rejection — resolves/rejects matchers
test("resolves matcher", async () => {
  await expect(fetchUser(1)).resolves.toMatchObject({ name: "Alice" });
});

test("rejects matcher", async () => {
  expect.assertions(1); // must run exactly 1 assertion
  await expect(fetchUser(-1)).rejects.toThrow("Not found");
});

// WHAT INTERVIEWERS LOOK FOR

The 'return or await' rule and why forgetting it causes false passes. The `resolves`/`rejects` matchers. `expect.assertions(n)` for testing error paths. This is a very practical interview topic because teams get burned by badly written async tests.

// COMMON PITFALL

Testing a rejection path without `expect.assertions(n)` — if the function resolves instead of rejects, the test passes with zero assertions, masking the bug.