Promises
If we use promises in our code, a more straightforward way to handle asynchronous tests exists. Hereafter this, you need to return a deposit from your test, and now Jest waits for that promise to resolve and complete. If, in this case where the contract is rejected, the test will fail automatically.
For instance, in fetchData, rather than using a callback, it returns a promise that we expect to resolve to the string 'ice cream'. We can test it with:
test('the data will be ice cream', () => {
return fetchData().then(data => {
expect(data).toBe('ice cream');
});
});
Here, we need to make sure that when we return the promise and edit the return statement, our test executes before the contract gets back to fetch data resolves and then() will have the chance to complete the callback.
If we expect a promise to be not accepted, we need to use the .catch method. Ensure you add an expectation and assertions to verify that a certain number of words are called.
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
.resolves/.rejects
In .resolves/.rejects we can use the .resolves matcher in the expect statement.In Jest, we will wait for that promise to resolve, and if the promise gets rejected, the test will automatically fail.
test('the data is ice cream', () => {
return expect(fetchData()).resolves.toBe('ice cream');
});
We need to make sure that we return the assertion-if this return statement is omitted, our test completes before the promise gets returned from fetch data which gets resolved, and then() we will get a chance to execute the callback. If a promise gets rejected, we need to use the .rejects matcher. It will work with the .resolves matcher. Now where the promise is fulfilled, the test will automatically fail.
test('The error will be displayed after the fetch fails', () => {
return expect(fetchData()).rejects.toMatch('error');
});
Async/Await
We can also use async and await our tests to make it more efficient. To write an async test, we need to use the async keyword with the function passed to the test. Like, the same fetch data scenario can be tested with the code below:
test('the data will be ice cream', async () => {
const data = await fetch data();
expect(data).toBe('ice cream');
});
test('an error gets displayed if fetch fails', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});
Now, we will combine async and await with .resolves or .rejects.
test('the data will be ice cream ', async () => {
await expect(fetchData()).resolves.toBe('ice cream');
});
test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toThrow('error');
});
We can mix and match them with our codebase or even within a single file. It depends upon the style that will make our test simpler.
Example
Let's implement a code that will fetch student data from an API and return the student name.
student.js
import request from './request';
export function getStudentName(studentID) {
return request('/students/' + studentID).then(student => student.name);
}
Now, we expect the request.js module to return a promise in the above implementation. We chain a call to them to receive the user name.
Now imagine an implementation of request.js that goes to the network and fetches some user data:
const HTTP = require('HTTP);
export default function request(url) {
return new Promise(resolve => {
// Below shows an example of http request, for example to fetch
student data from an API.
http.get({path: url}, response => {
let data = '';
response.on('data', _data => (data += _data));
response.on('end', () => resolve(data));
});
});
We will create a manual mock for our request.js module in the __mocks__ folder.
const students = {
1: {name: 'Arav'},
2: {name: 'Shiv'},
};
export default function request(url) {
return new Promise((resolve, reject) => {
const studentID = parseInt(url.substr('/students/'.length), 10);
process.nextTick(() =>
students[studentID]
? resolve(students[studentID])
: reject({
error: 'Student with ' + studentID + ' not found.',
}),
);
});
Now, we will check the async functionality by using the following code.
jest.mock('../request');
import * as student from '../student';
// The assertion for promise must be returned.
it('works with promises', () => {
expect.assertions(1);
return student.getStudentName(1).then(data => expect(data).toEqual('Arav'));
});
We can chain as many promises as we like and call expect at any time, as long as we return a Promise at the end.
.resolves
By using resolves we unwrap the value of a fulfilled promise together with other matcher. If the promise is rejected, the assertion will fail.
it('works with resolves', () => {
expect.assertions(1);
return expect(user.getStudentName*(2))).resolves.toEqual('Shiv');
});
async/await
That’s how we are going to write the code.
// async/await can be used.
it('works with async/await', async () => {
expect.assertions(1);
const data = await student.getStudentName(1);
expect(data).toEqual('Arav');
});
// async/await is used with `.resolves`.
it(' here it works with async/await and resolves', as () => {
expect.assertions(1);
await expect(user.getStudentName(2)).resolves.toEqual('Shiv');
});
FAQs
-
Why is asynchronous testing done?
When we have a code that runs asynchronously, Jest needs to know when the code it is testing has been completed before moving on to another test. To solve this issue we use asynchronous testing.
-
What will happen if a promise never gets resolved?
If a promise doesn't get resolved, our promise will never get executed and we won’t get the desired result.
Key Takeaways
This blog was all about asynchronous testing and why it's necessary. It also included the possibility of code running error-free and without redundancies with all possible outcomes. Through this blog, a clear vision of asynchronous testing and its working is portrayed. This will help you make an application run better and crack interviews.
Please read our other blogs related to testing like Snapshot Testing, Usability Testing, etc.
We hope you found this blog on asynchronous testing useful. Liked the blog? Then feel free to upvote and share it.