Jest · Unit tests that survive refactors

How do you test asynchronous code in Jest?

MediumasyncawaittestingUpdated Oct 2026
Quiz this track

Short answer

Make the test async and await the operation before asserting. Return or await every promise so Jest waits, and use findBy or waitFor for UI that updates after async work.

The most common async bug is a test that finishes before its assertion runs. Jest sees a synchronous test return and marks it passed; the assertion then rejects later, after the test is over. Awaiting or returning the promise is what makes Jest wait for it.

ts
test("returns the created user", async () => {
  const api = { create: jest.fn().mockResolvedValue({ id: "u1" }) };

  const user = await createUser(api, "asha@example.com");

  expect(user.id).toBe("u1");
  expect(api.create).toHaveBeenCalledTimes(1);
});

Ways to wait

  • async test plus await for promises
  • Return the promise when the test body is not async
  • await expect(promise).resolves or .rejects for settlement assertions
  • await screen.findByRole(...) for UI that appears later
  • await waitFor(() => ...) when a condition becomes true over time

The done callback still exists for callback-style APIs, but it is easy to misuse: calling done without handling an error, or forgetting it entirely, causes a timeout instead of a clear failure. Prefer promises and async functions for new tests. If a callback is unavoidable, wrap it in a promise helper.

Never use a fixed sleep to wait for async work. A sleep is slower than necessary on a fast machine and still flaky on a slow one. Wait for the observable condition instead.

Follow-ups you should expect

Interviewers use these to check whether the first answer was understanding or memorisation.

  1. 1What happens if you forget to await an async assertion?
  2. 2When is the done callback still useful?
  3. 3Why is a fixed sleep worse than waitFor?
  4. 4How do you test that an async operation rejects?

Say it in the interview

I make the test async and await the code under test before asserting, so Jest knows when the test is finished. For promises I use resolves or rejects, and for UI I use findBy or waitFor on an observable condition. I avoid the done callback unless I am testing a callback-style API, and I never use a fixed sleep. If an assertion is not awaited, the test can pass while it fails later.

Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.

Related questions

Reading is step one

Now say it out loud.

Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Jest mock interview and get scored feedback on depth, clarity and confidence.