Async Testing with Promises

Jest and Vitest easily support testing of async functions and promises.

The easiest way is to turn your test() or it() callback (their 2nd argument) into an async function and simply await the result

In the examples on this page I'll use setTimeout() to simulate async behaviour. We will have a lesson further on on how to deal with mock timers btw.

A very common async behaviour you will encounter is mocking fetch() calls - again we will cover this in a later lesson.

function calculateAsync(a, b) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(a + b);
    }, 100);
  });
}

// note: the test fn is async
test('it adds two numbers', async () => {
  expect(
    await calculateAsync(10, 100)
  ).toBe(110);
});

In this lesson we'll go into more detail on the ways to work with async functions and promises.

Simple async test

As shown in the example above, you can just call await within your test function

test('it adds two numbers', async () => {
  expect(
    await calculateAsync(10, 100)
  ).toBe(110);
});

You can also await the expect function, and use .resolves along with a regular matcher (like .toBe(), .toStrictEqual() etc)

test('it adds two numbers', async () => {
  await expect(
    calculateAsync(10, 100)
  ).resolves.toBe(110);
});

Testing async function throws (promise rejections)

To test that an async function rejects (e.g. if it throws something, or a Promise was marked as rejected), you use .rejects.toThrow()

function divideAsync(
  val,
  divideByValue
) {
  return new Promise(
    (resolve, reject) => {
      setTimeout(() => {
        if (divideByValue === 0) {
          reject(
            new Error(
              'Division by zero'
            )
          );
        } else {
          resolve(val / divideByValue);
        }
      }, 25);
    }
  );
}

test('async function will throw', async () => {
  await expect(
    divideAsync(100, 0)
  ).rejects.toThrow('Division by zero');
});

Lesson Task

In this test the async function waits ~25ms, then either resolves with a value or rejects with an error.

You should use .resolves.toBe(...) for the success case, or .rejects.toThrow(...) for the error case.

Ready to try running tests for this lesson?