Skip to content

Testing Promises in Jest

Functions that return a Promise need to be tested in a way that waits for the promise to settle. This lesson focuses specifically on testing promise-returning functions with Jest's .resolves/.rejects matchers.

Testing with .resolves and .rejects

.resolves unwraps a promise's fulfilled value before applying a matcher, and .rejects unwraps a rejected promise's error. Both require await (or return) in front of the expect() call, since they are themselves asynchronous.

These modifiers are usually cleaner than manually chaining .then()/.catch() inside the test body, especially for simple resolve/reject assertions.

function fetchUser(id) {
  if (id <= 0) return Promise.reject(new Error('Invalid id'));
  return Promise.resolve({ id, name: 'Ada' });
}

test('resolves with a user for a valid id', async () => {
  await expect(fetchUser(1)).resolves.toEqual({ id: 1, name: 'Ada' });
});

test('rejects for an invalid id', async () => {
  await expect(fetchUser(-1)).rejects.toThrow('Invalid id');
});

Both tests use await before expect() because .resolves/.rejects return their own promise that must be waited on.

Alternative: Returning a .then() Chain

test('resolves with a user (alternative style)', () => {
  return fetchUser(1).then((user) => {
    expect(user).toEqual({ id: 1, name: 'Ada' });
  });
});
  • Returning a .then() chain is an older but still valid pattern, predating .resolves/.rejects.
  • .resolves/.rejects are generally preferred today for their concise, matcher-friendly syntax.
  • Both approaches require return (or await) so Jest waits for the promise to settle.
  • For multiple assertions on the same resolved value, async/await with a variable is often clearest.

Testing Promises Cheat Sheet

Every common pattern for testing promise-based code.

Goal Pattern
Assert a resolved value await expect(promise).resolves.toBe(x)
Assert a rejection await expect(promise).rejects.toThrow()
Assert on a resolved value's shape await expect(promise).resolves.toMatchObject({...})
Chain-based alternative return promise.then((v) => expect(v).toBe(x))
Multiple assertions on one value const v = await promise; expect(v)...; expect(v)...

Multiple Assertions on the Same Resolved Value

When you need several assertions on one resolved value, awaiting the promise into a variable first (instead of chaining several .resolves calls) keeps the test more readable and avoids awaiting the same promise multiple times.

test('returns a fully-formed user object', async () => {
  const user = await fetchUser(1);

  expect(user.id).toBe(1);
  expect(user.name).toBe('Ada');
  expect(user).toHaveProperty('name');
});

Testing Code That Uses Promise.all()

When testing a function that awaits several promises concurrently with Promise.all(), you can mock each dependency independently and simply await the combined function's result as usual — Jest doesn't need special handling for Promise.all() itself.

test('fetches user and posts concurrently', async () => {
  fetchUser.mockResolvedValue({ id: 1 });
  fetchPosts.mockResolvedValue([{ id: 10 }]);

  const result = await loadDashboard(1);

  expect(result).toEqual({ user: { id: 1 }, posts: [{ id: 10 }] });
});

Common Mistakes

  • Forgetting await before expect(promise).resolves.../.rejects....
  • Using .resolves on a promise you expect to reject (or vice versa), producing a confusing failure.
  • Chaining many .resolves calls on the same promise instead of awaiting it once into a variable.
  • Not testing the rejection path for functions that can realistically fail.

Key Takeaways

  • .resolves/.rejects unwrap a promise's outcome before applying a matcher.
  • Both require await or return since they return their own promise.
  • Returning a .then() chain is a valid, older alternative to .resolves/.rejects.
  • Awaiting into a variable is cleanest when making multiple assertions on one resolved value.

Pro Tip

Default to async/await with a plain expect() on an awaited variable for anything beyond a single simple assertion — it reads more naturally than chaining multiple .resolves calls.