Testing Async Functions in Jest
First, you need to enable Babel support in Jest as documented in our tutorial for getting started with Jest.
We will implement a simple module that fetches user data from an API and then returns the user's age.
// user.js
import request from './request';
export function getUserName(userID) {
return request('/users/' + userID).then(user => user.age);
}
In the implementation above, we expect the request.js module to return a promise. We chain a call to then to receive the user's age.
Now consider an implementation of request.js that goes to the network and fetches some user data:
// request.js
const http = require('http');
export default function request(url) {
return new Promise(resolve => {
// An example of an http request, for instance to fetch
// user data from an API.
// This module is being mocked in __mocks__/request.js
http.get({path: url}, response => {
let data = '';
response.on('data', _data => (data += _data));
response.on('end', () => resolve(data));
});
});
}
Since we don't want to go to the network in our test, we will create a manual mock for our request.js module in the __mocks__ folder (this folder is case-sensitive, so __MOCKS__ will not work). It could look something like this:
// __mocks__/request.js
const users = {
4: {age: 23},
5: {age: 25},
};
export default function request(url) {
return new Promise((resolve, reject) => {
const userID = parseInt(url.substr('/users/'.length), 10);
process.nextTick(() =>
users[userID]
? resolve(users[userID])
: reject({
error: 'User with ' + userID + ' not found.',
}),
);
});
}
Now let us write a test for our async functionality.
// __tests__/user-test.js
jest.mock('../request');
import * as user from '../user';
// The assertion for a promise needs to be returned.
it('works with promises', () => {
expect.assertions(1);
return user.getUserName(4).then(data => expect(data).toEqual('Mark'));
});
We call jest.mock('../request') and this tells Jest to use our manual mock. it will expect the return value to be a Promise that is going to be resolved. You can chain as many Promises as you want and then call expect at any time, as long as you will return a Promise at the end.
.resolves
There is a less verbose way that uses resolves to unwrap the value of a fulfilled promise together with any other matcher. In the case where the promise is rejected, the assertion fails.
it('works with resolves', () => {
expect.assertions(1);
return expect(user.getUserName(5)).resolves.toEqual('Paul');
});
async/await
It is easy to write tests using the async/await syntax. You can write the same examples as before this way:
// async/await can be used.
it('works with async/await', async () => {
expect.assertions(1);
const data = await user.getUserName(4);
expect(data).toEqual('Mark');
});
// async/await can also be used with `.resolves`.
it('will work with async/await and resolves', async () => {
expect.assertions(1);
await expect(user.getUserName(5)).resolves.toEqual('Paul');
});
If you want to enable async/await in your project, you need to install @babel/preset-env and you will enable the feature in your babel.config.js file.
Error handling
You can handle errors using the .catch method. You need make sure you add expect.assertions to verify that a certain number of assertions are called. Otherwise a fulfilled promise will not fail the test:
// Testing for async errors using Promise.catch.
test('tests error with promises', () => {
expect.assertions(1);
return user.getUserName(2).catch(e =>
expect(e).toEqual({
error: 'User with 2 not found.',
}),
);
});
// Or using async/await.
it('tests error with async/await', async () => {
expect.assertions(1);
try {
await user.getUserName(1);
} catch (e) {
expect(e).toEqual({
error: 'User with 1 not found.',
});
}
});
.rejects
The.rejects helper also works like the .resolves helper. In the case where the promise is fulfilled, the test automatically fails.
// Testing for async errors using `.rejects`.
it('tests error with rejects', () => {
expect.assertions(1);
return expect(user.getUserName(3)).rejects.toEqual({
error: 'User with 3 not found.',
});
});
// Or using async/await with `.rejects`.
it('will test error with async/await and rejects', async () => {
expect.assertions(1);
await expect(user.getUserName(3)).rejects.toEqual({
error: 'User with 3 not found.',
});
});
Previous:
Using Timer Mocks in Jest.
Next:
Mocking ES6 Classes in Jest.
It will be nice if you may share this link in any developer community or anywhere else, from where other developers may find this content. Thanks.
https://w3resource.com/jest/an-async-example.php
- Weekly Trends and Language Statistics
- Weekly Trends and Language Statistics