Testing Asynchronous Code
Return a promise from your test, and Jest will wait for that promise to resolve. If the promise is rejected, the test will fail.
For example, let’s say that fetchData
returns a promise that is supposed to resolve to the string 'peanut butter'
. We could test it with:
Alternatively, you can use async
and await
in your tests. To write an async test, use the async
keyword in front of the function passed to test
. For example, the same fetchData
scenario can be tested with:
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
} catch (e) {
expect(e).toMatch('error');
}
});
You can combine async
and await
with .resolves
or .rejects
.
In these cases, and await
are effectively syntactic sugar for the same logic as the promises example uses.
caution
If you expect a promise to be rejected, use the .catch
method. Make sure to add expect.assertions
to verify that a certain number of assertions are called. Otherwise, a fulfilled promise would not fail the test.
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
If you don’t use promises, you can use callbacks. For example, let’s say that fetchData
, instead of returning a promise, expects a callback, i.e. fetches some data and calls callback(null, data)
when it is complete. You want to test that this returned data is the string 'peanut butter'
.
By default, Jest tests complete once they reach the end of their execution. That means this test will not work as intended:
The problem is that the test will complete as soon as fetchData
completes, before ever calling the callback.
There is an alternate form of test
that fixes this. Instead of putting the test in a function with an empty argument, use a single argument called done
. Jest will wait until the done
callback is called before finishing the test.
test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
}
try {
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
If done()
is never called, the test will fail (with timeout error), which is what you want to happen.
caution
Jest will throw an error, if the same test function is passed a done()
callback and returns a promise. This is done as a precaution to avoid memory leaks in your tests.
You can also use the .resolves
matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail.
Be sure to return the assertion—if you omit this return
statement, your test will complete before the promise returned from fetchData
is resolved and then() has a chance to execute the callback.
If you expect a promise to be rejected, use the .rejects
matcher. It works analogically to the .resolves
matcher. If the promise is fulfilled, the test will automatically fail.
test('the fetch fails with an error', () => {
});
None of these forms is particularly superior to the others, and you can mix and match them across a codebase or even in a single file. It just depends on which style you feel makes your tests simpler.