Spectral now part of Check Point’s CloudGuard to provide the industry’s most comprehensive security platform from code to cloud Read now

Jest async test: A developer’s tutorial

By Eyal Katz April 5, 2023

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.

Javascript variable value meme

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.

test('fetchData returns expected data', async () => {
  const result = await fetchData();
  expect(result).toEqual(APIData);
});

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: 

test('fetchData returns the correct data', async () => {
  const mockFetch = { id: 1, name: 'John Doe' };
  global.fetch = jest.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve(mockFetch),
    })
  );
  const data = await fetchData();
  expect(data).toEqual(mockFetch);
});

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: 

  1. 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. 
  1. 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.
  1. 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);
});
  1. 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.
  1. 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.
jest.mock('axios', () => ({
  get: jest.fn(() => Promise.resolve({ data: 'mock data' }))
}));
  1. 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();
});
  1. 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');
});
  1. 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();
});
  1. 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);
});
  1. 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.

That moment when your code works meme

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.

Related articles

how does infrastructre as code on aws work

How Does Infrastructure as Code on AWS work? 

Imagine having to manually provision and configure every device in a large corporation. Then visualize the upgrade process. How about patching? Then, picture ensuring conformity on

6 Use Cases for Distributed Deep Learning

6 Use Cases for Distributed Deep Learning

In the world of Big Data, decentralization is king.  Seeing that today’s data sets are so large and complex that traditional database systems cannot handle them,

best software deployment tools for 2022

Top 10 Software Deployment Tools for 2024

Updated 03.24 Approaching any finish line in life can be exciting yet stressful. This holds especially true in software deployment. The deployment phase is the final

Stop leaks at the source!