Dealing with async jest tests

jest Sep 25, 2020

In jest, there are a couple of different ways to handle tests which deal with async code.

Let's say way have a test that calls fetchSomeAsyncThing().

it('should call my api', () => {
  const result = fetchSomeAsyncThing();

  expect(result.foo).toBe('hello world')
});

This will fail.

Why?

Because of the way JavaScript handles async code.

When you call a function known to be async, whether it's something that returns a Promise, calls a setTimeout, etc, that function get pushed onto a call stack, and called wayyy after your tests have actually ran.

A stack is a data structure kinda like an array which basically means, the last thing that got put into it, is the first thing to come out. Read more about stacks and async Javacript.

Use the done callback

If your async function returns a callback, several of the jest functions provide a done callback which you can use like this...

it('should call my api', (done) => {
  const result = fetchSomeAsyncThing((result) => {
    expect(result.foo).toBe('hello world');
      done();
  });
});

As you can see, the callback to the it method above returns a done function.

This done function will tell jest that your async method is done and it will wait for that all to finish before wrapping up that test.

Use promises

If your async function returns promises you can do one of 2 things.

it('should call my api', (done) => {
  const result = fetchSomeAsyncThing().then((result) => {
    expect(result.foo).toBe('hello world');
      done();
  });
});

Similar to above, use the done function inside the .then.

Or, you can simply return the entire promise.

it('should call my api', () => {
  return fetchSomeAsyncThing((result) => {
    expect(result.foo).toBe('hello world');
  });
});

In this case, jest will realize that the return value of the test was itself a promise, and will therefore wait until that promise fully resolves before wrapping up the test.

Use async / await

Once again, if you know that your async function returns a promise, you can use the async and await features of modern Javascript.

it('should call my api', async () => {
  const result = await fetchSomeAsyncThing();
  expect(result.foo).toBe('hello world');
});

The async / await method is a nice, clean way to read the code.

Tags