Jest is one of the most commonly used test frameworks for JavaScript testing. With the rise of asynchronicity in modern web development, it’s important to know how to test your async code properly to ensure your application runs smoothly.
Asynchronous testing has been a pain point for many developers starting out with Jest. But by integrating Jest’s testing capabilities into the DevOps lifecycle, developers can ensure that their code is thoroughly tested and meets their quality standards before deploying it into production. By the end of this article, you will have a comprehensive understanding of how to write effective and efficient async tests with Jest.
Async and Jest, why the match is made in heaven
In the context of JavaScript, asynchronicity refers to the ability to execute code in a non-blocking way, allowing other code to run while a particular operation is waiting for a response or completing some task.
Jest is particularly helpful for testing asynchronous code because it provides powerful tools for handling asynchronous patterns like callbacks, promises, and async/await. Developers can use Jest’s done callback to test callback-based code, and Jest’s resolves and rejects matchers to test promises.
For async/await, developers can declare their test function as async and use the await keyword to wait for asynchronous operations to complete. Jest will automatically detect when the test function returns a promise and wait for it to resolve or reject.
Using Jest, developers can easily test their asynchronous code, catch bugs early in the development process, and build more reliable and efficient applications. That’s why the combination of asynchronous programming and Jest’s testing tools make a match in heaven.
An async example
You can use Jest async tests in many scenarios where JavaScript code executes asynchronously. Using setTimeout, setInterval, or making requests to a remote server are examples of such use cases.
Let’s see an example of using async when testing a function that makes a request to a remote API to fetch data.
Consider the following function that fetches data from a remote API:
async function fetchData() {
const response = await fetch('https://yourwebsite.com/data');
const data = await response.json();
return data;
}
You can understand what we are doing with the above code. We call the fetch method to send a request to the remote API. Then we parse the received response as a JSON. The await keyword makes the code wait for each asynchronous operation to complete before continuing with the next line. You can easily test this function using the await keyword. Look at the code below.
Here, the async keyword is used to indicate the function is asynchronous, and the await keyword is used to wait for the fetchData function to complete. Then we use the expect function to assert that the returned data matches the expected result.
By using Jest async tests in this way, developers can ensure that their code works as expected in scenarios involving asynchronicity. This overall ensures that their applications are reliable, efficient, and performant, even in complex and dynamic environments.
Jest Vocabulary: Mock and Spy
There are two important concepts in Jest called Mock and Spy. Mocks are fake functions that allow you to test a code isolated from its dependencies. For example, consider the example we used in the above section. In that example, we made an API call, and we had to wait until the API sent a response to test the rest of the functionality of our code.
With Jest, we can create a mock function that mimics the API call and returns fake data. This way, we can test our code without actually making a request to the API. It increases the efficiency of the testing as we don’t have to wait for delays from the third-party external APIs.
Here is our code again:
async function fetchData() {
const response = await fetch('https://yourwebsite.com/data');
const data = await response.json();
return data;
}
Here is our test code which creates a Mock for the API call:
jest.fn() creates a mock and returns fake API data for the fetch function in the FakeData function. Therefore, your testing code won’t be affected by any issues the external API may cause.
Spies allow you to observe how other functions are used in your code. You can use spies to verify that a function was called, how many times it was called, what arguments were passed to it, and other details. You can use jest.spyOn() for creating spies.
Jest Async Testing: A Step-by-Step Guide
Testing asynchronous code with Jest can be a bit tricky. If you write bad code, your application may have many undetected bugs. To write efficient code, you must ensure high-quality test codes. Here are 10 steps to help you:
Ensure your project environment is properly configured for Jest. you can install Jest with npm and ensure you have installed the node-fetch package to send API requests.
Create a new test file for the function or module you want to test. Start your Jest watch command by running the npm test in the terminal. This will watch your files and rerun your tests as you make changes to your code.
Use the async/await syntax to write your test function. This allows you to write your test as if it were synchronous code while still testing the asynchronous function.
test('async function should return the correct value', async () => {
const result = await myAsyncFunction();
expect(result).toBe(expectedValue);
});
To mock an API request, you can use Jest’s jest.mock() function. This allows you to replace the original function with a mocked version that returns the expected data.
Add Jest mock for Axios: If you’re using Axios to make API requests, you can use the jest.mock() function to mock the Axios module. This allows you to replace the original Axios module with a mocked version.
Use Jest’s toHaveBeenCalled() matcher to verify if a mocked function has been called. This is useful for testing if an API request was made.
test('check if mock function has been called', async () => {
const fetchData = jest.fn();
await fetchData();
expect(fetchData).toHaveBeenCalled();
});
When testing Promises, use Jest’s resolves and rejects matchers to verify that the Promise is resolved or rejected with the expected data.
test('test a Promise with Jest matchers', async () => {
const myPromise = Promise.resolve('value');
await expect(myPromise).resolves.toBe('value');
});
Use Jest’s fakeTimers when testing code that relies on timers to simulate the passage of time. This allows you to test your code without having to wait for the actual timer to complete.
test('test code that relies on timers', () => {
jest.useFakeTimers();
const callback = jest.fn();
setTimeout(callback, 1000);
jest.runAllTimers();
expect(callback).toHaveBeenCalled();
});
Use Jest’s done function when testing code that relies on callbacks to inform Jest that the test has finished. Call the done function after your assertions to ensure that Jest waits for the callback to complete before finishing the test.
test('test code that relies on callbacks', done => {
const callback = () => {
expect(true).toBe(true);
done();
};
myAsyncFunction(callback);
});
Use Jest’s beforeAll, afterAll, beforeEach, and afterEach functions to set up and tear down test fixtures. This is useful for preparing data or resources that need to be available for all tests.
beforeAll(() => {
// set up test environment
});
afterAll(() => {
// tear down test environment
});
beforeEach(() => {
// prepare data or resources
});
afterEach(() => {
// clean up data or resources
});
Aside from following these important steps when testing asynchronous code using Jest, remember to keep your tests fast and modular, and isolate individual components of your code.
Coding with confidence
Writing tests for asynchronous code can be challenging, but Jest simplifies your job with inbuilt functions and different approaches. We’ve seen how to do async testing with Jest and the importance of Mock and Spy when working with external dependencies such as APIs. By following these steps when implementing your test cases, you won’t have many problems testing JavaScript codes efficiently. However, testing alone can’t ensure code safety.
Spectral helps developers to ensure code security without sacrificing the simplicity and speed of your development process. Our scanning engine combines AI and hundreds of detectors to monitor, classify, and protect you from exposed API keys, tokens, credentials, and security misconfigurations at build time. To see it for yourself, launch a free code scan and get started today.
Kubernetes powers significant automation capabilities for developers in deploying, managing, scaling, and ensuring the availability of containerized apps. Data from 2021 shows that adoption continues to
Security is the biggest threat facing organizations that strive for faster software delivery. Organizations are witnessing increasing attacks due to application code gaps and security weaknesses.
While modern businesses depend on data to stay ahead of the competition, data alone isn’t enough. They also need efficient search engines to quickly index and