📣 Requestly API Client – Free Forever & Open Source. A powerful alternative to Postman. Try now ->

Mock API Calls in Jest: Complete Guide with Examples

Rohit Rajpal
Learn how to mock API calls in Jest with examples that show how to create mock data, test failures, add delays, and build real-world workflows.
Mock API Calls in Jest Complete Guide with Examples

When writing tests, one of the biggest challenges is deciding how much to depend on real APIs. A live API may give the most accurate picture, but it also brings in uncertainty: data can change, requests may hit rate limits, or tests may slow down because of network calls. This creates friction for developers who want quick feedback loops and for testers who need reproducible results.

This is where mock API calls in Jest play a role. Instead of hitting actual endpoints, you can intercept calls and return predefined responses. This makes tests faster, isolates them from external dependencies, and gives full control over edge cases that are difficult to trigger with real services.

This guide explains how to set up mocks and how to use them to create meaningful test coverage for real-world scenarios.

What Are Mock API Calls in Jest?

Mock API calls in Jest are simulated versions of network requests. Instead of sending a request over the network, Jest intercepts the call and returns a controlled response. This allows tests to run without depending on the availability or behavior of real APIs.

At a basic level, a mock replaces the function that makes the request. Depending on the setup, the mock can return static data, dynamic results based on input, or even errors. This flexibility helps developers and testers create scenarios that are hard to reproduce with live APIs.

Here are the core aspects that define mock API calls in Jest:

  • Interception of network calls: The actual HTTP request is never made. Jest replaces it with a mock function or module.
  • Custom responses: Mocks can be configured to return fixed data, error objects, or sequential responses across multiple calls.
  • Predictable behavior: Since responses are defined in the test, the outcome is consistent every time, regardless of external changes.
  • Faster test runs: Tests skip the network layer, which reduces execution time and helps keep feedback loops short.

By controlling these elements, Jest makes it possible to focus on testing how the application handles responses, not whether the API is up or returning the expected payload.

Why Mock API Calls in Jest?

Mocking API calls in Jest helps improve the reliability and control of automated tests. It removes external dependencies and allows teams to create scenarios that are otherwise hard to reproduce with live services.

Here are a few more reasons developers and testers use mocking in Jest:

  • Consistency in test results: Mocks return predictable responses every time, so test outcomes remain stable across runs.
  • Coverage of edge cases: Scenarios such as timeouts, 404 errors, or invalid payloads can be validated without needing the backend to misbehave.
  • Faster development cycles: Tests execute quickly when the network layer is skipped, which shortens the feedback loop.
  • Isolation of functionality: Client-side logic can be tested independently, without backend delays or unexpected changes.
  • Scalability in large test suites: Projects with hundreds of tests stay manageable when mocks replace live API calls.

How to Mock API Calls in Jest?

Mocking API calls in Jest can be done in several ways, depending on whether you want to replace an entire module, override a specific function, or simulate asynchronous behavior.

Below are the core methods for mocking API calls in Jest:

  • Mocking API calls using jest.mock(): Replaces an entire module with a mock implementation, commonly used for libraries like Axios or Fetch wrappers.
  • Mocking Axios requests in Jest: Allows control over request and response behavior for codebases that rely on Axios.
  • Using jest.fn() for API function mocks: Creates a mock version of a function and defines custom return values, useful for smaller, direct API calls.
  • Handling async API calls and promises: Ensures tests can handle both resolved and rejected promises without waiting for real network responses.

Below is a detailed explanation of how to mock API calls in Jest using these methods.

1. Mocking API Calls Using jest.mock()

jest.mock() is the most common way to replace an entire module with a mock. This is useful when your code imports a library like axios or a custom module for making HTTP requests. Instead of calling the real implementation, Jest replaces it with a mock that you can fully control in your test file.

Here’s a step-by-step look at how it works:

1. Import the module you want to mock

Jest automatically hoists jest.mock() calls to the top of the file, so the module is replaced before any code runs.

				
					import axios from 'axios';
import fetchUsers from './fetchUsers'; // function that calls axios.get internally

jest.mock('axios'); // axios is now mocked
				
			

2. Define the mock implementation

You can tell the mocked module what to return. For example, if your code calls axios.get, you set up a resolved value.

				
					 test('fetchUsers returns data from API', async () => {
  const mockResponse = { data: [{ id: 1, name: 'John Doe' }] };
  axios.get.mockResolvedValue(mockResponse);

  const users = await fetchUsers();
  expect(users).toEqual(mockResponse.data);
});
				
			

In this case, axios.get never makes a network call. It immediately resolves with the mock response.

3. Simulate error cases

You can also test how your code handles API failures by mocking a rejected promise.

				
					test('fetchUsers handles API errors', async () => {
  axios.get.mockRejectedValue(new Error('Network error'));

  await expect(fetchUsers()).rejects.toThrow('Network error');
});
				
			

4. Control multiple calls

If your function makes several requests, you can define different responses for each call.

				
					 test('fetchUsers handles multiple API calls', async () => {
  axios.get
    .mockResolvedValueOnce({ data: [{ id: 1 }] })
    .mockResolvedValueOnce({ data: [{ id: 2 }] });

  const firstCall = await fetchUsers();
  const secondCall = await fetchUsers();

  expect(firstCall).toEqual([{ id: 1 }]);
  expect(secondCall).toEqual([{ id: 2 }]);
});
				
			

2. Mocking Axios Requests in Jest

Many applications use Axios as the HTTP client, so controlling its behavior in tests is important. While jest.mock() can replace the entire Axios module, sometimes you need finer control over request and response handling. Jest allows you to do this by mocking only the parts of Axios you rely on.

Here’s how developers typically set it up:

1. Mock the Axios module

By default, jest.mock(‘axios’) turns Axios into a mock where each function (get, post, etc.) can be defined separately.

				
					import axios from 'axios';
import fetchPosts from './fetchPosts'; // internally calls axios.get

jest.mock('axios');
				
			

2. Define responses for specific methods

Each method in Axios becomes a mock function. For example, if your function calls axios.get, you can make it return a controlled response.

				
					test('fetchPosts returns data from API', async () => {
  const mockResponse = { data: [{ id: 101, title: 'First Post' }] };
  axios.get.mockResolvedValue(mockResponse);

  const posts = await fetchPosts();
  expect(posts).toEqual(mockResponse.data);
});
				
			

This ensures that fetchPosts is tested without any real network request.

3. Mock multiple Axios methods

If your code uses different request types, you can mock each one individually.

				
					test('handles POST and GET with Axios', async () => {
  axios.post.mockResolvedValue({ data: { success: true } });
  axios.get.mockResolvedValue({ data: [{ id: 1 }] });

  const postResult = await axios.post('/save', { name: 'test' });
  const getResult = await axios.get('/items');

  expect(postResult.data.success).toBe(true);
  expect(getResult.data).toEqual([{ id: 1 }]);
});
				
			

4. Simulate errors with Axios

Since real APIs fail unpredictably, mocking errors lets you test resilience.

				
					test('handles Axios error response', async () => {
  axios.get.mockRejectedValue({ response: { status: 404 } });

  try {
    await fetchPosts();
  } catch (err) {
    expect(err.response.status).toBe(404);
  }
});
				
			

3. Using jest.fn() for API Function Mocks

In many projects, API requests are not made directly inside components or services. Instead, they are wrapped in helper functions so the rest of the codebase does not depend on raw fetch or axios. This setup makes it easier to replace the helper with a Jest mock during testing.

With jest.fn(), you can directly create a mock version of such helpers. Unlike jest.mock(), which replaces an entire module, jest.fn() is useful when you want to mock only specific functions without touching the rest of the file.

Here are the key ways jest.fn() helps with API call mocking:

1. Replacing a Function with a Mock

Suppose you have a helper that fetches users:

				
					// userApi.js
export async function fetchUsers() {
  const res = await fetch("/users");
  return res.json();
}
				
			

In your tests, you can replace this function with a simple mock:

				
					import * as userApi from "./userApi";

test("displays users", async () => {
  userApi.fetchUsers = jest.fn().mockResolvedValue([{ id: 1, name: "John" }]);
  
  // rest of the test runs with the mocked data
});
				
			

Here, jest.fn().mockResolvedValue ensures that whenever fetchUsers is called, it returns the specified mock data instead of hitting the real endpoint.

2. Returning Different Values for Multiple Calls

Sometimes you want different responses across calls, for example, when testing pagination or retries. With jest.fn(), you can chain behaviors:

				
					userApi.fetchUsers = jest
  .fn()
  .mockResolvedValueOnce([{ id: 1 }])   // first call
  .mockResolvedValueOnce([{ id: 2 }]);  // second call
				
			

This way, you can test how the component handles sequential requests without having to set up multiple tests.

3. Simulating Rejected API Calls

To test error handling, you can mock a rejection:

				
					userApi.fetchUsers = jest.fn().mockRejectedValue(new Error("Network error"));
				
			

This allows you to verify whether your application displays an error message, retries the request, or logs the failure correctly.

4. Handling Async API Calls and Promises in Jest

Most API interactions are asynchronous, which means that simply calling the function in a test is not enough. If the test finishes before the promise resolves, it may pass or fail incorrectly. Jest provides multiple ways to handle async calls so that the test waits for the result before asserting.

1. Using async/await

The most common pattern is to write your tests as async functions and use await with your mocked API.

				
					test("fetches users with async/await", async () => {
  userApi.fetchUsers = jest.fn().mockResolvedValue([{ id: 1, name: "John" }]);

  const users = await userApi.fetchUsers();
  expect(users).toEqual([{ id: 1, name: "John" }]);
});
				
			

Here, await ensures the test does not move to the expect until the promise resolves.

2. Using .resolves and .rejects

Jest also provides matchers for promises. Instead of using await, you can return the promise directly from the test.

				
					test("resolves with users", () => {
  userApi.fetchUsers = jest.fn().mockResolvedValue([{ id: 2, name: "Jane" }]);

  return expect(userApi.fetchUsers()).resolves.toEqual([{ id: 2, name: "Jane" }]);
});
				
			

For errors:

				
					test("rejects with error", () => {
  userApi.fetchUsers = jest.fn().mockRejectedValue(new Error("Network down"));

  return expect(userApi.fetchUsers()).rejects.toThrow("Network down");
});
				
			

3. Using .mockImplementation for Complex Flows

Sometimes the response needs more logic than a static resolved or rejected value. For example, simulating a delay before returning.

				
					userApi.fetchUsers = jest.fn().mockImplementation(() => 
  Promise.resolve([{ id: 3, name: "Alice" }])
);
				
			

This flexibility makes it easier to mimic backend logic, like conditional responses or delayed network behavior.

Creating Mock Data and Responses

After setting up mocks, the next step is to decide what data they should return. The structure of this data directly affects how realistic and useful the tests will be.

Here are different types of mock data for both common cases and edge scenarios.

1. Static Mock Data for API Responses

Static mocks are hardcoded responses that never change. They are useful when you want your tests to be predictable and focused on one behavior.

				
					const mockUsers = [
  { id: 1, name: "John" },
  { id: 2, name: "Jane" },
];

userApi.fetchUsers = jest.fn().mockResolvedValue(mockUsers);
				
			

Here, every call to fetchUsers() will return the same list. This is ideal for smoke tests or when validating that the UI correctly renders known data.

2. Dynamic Mock Responses Based on Input

Static mocks can fall short when your function behaves differently depending on input. With Jest, you can return responses conditionally.

				
					userApi.fetchUserById = jest.fn((id) => {
  if (id === 1) return Promise.resolve({ id: 1, name: "John" });
  if (id === 2) return Promise.resolve({ id: 2, name: "Jane" });
  return Promise.reject(new Error("User not found"));
});
				
			

This pattern mirrors backend logic and helps you test error handling paths without modifying the production API.

3. Sequential Responses for Multiple API Calls

Some tests involve multiple calls to the same endpoint, such as polling or pagination. Jest supports queuing responses for sequential calls.

				
					userApi.fetchUsers = jest
  .fn()
  .mockResolvedValueOnce([{ id: 1, name: "John" }]) // first call
  .mockResolvedValueOnce([{ id: 2, name: "Jane" }]); // second call
				
			

This approach ensures the test can mimic how the app behaves when the data changes over time, like refreshing lists or fetching more pages.

4. Partial Mocks to Preserve Original Functionality

Sometimes you only want to override one method in a module while keeping others intact. Jest allows partial mocks using jest.spyOn.

				
					import * as userApi from "./userApi";

jest.spyOn(userApi, "fetchUsers").mockResolvedValue([{ id: 3, name: "Alice" }]);
				
			

Here, fetchUsers is replaced with a mock, but the rest of the userApi module still behaves normally. This is useful in larger codebases where replacing the entire module would break unrelated tests.

Testing Real-World API Scenarios in Jest

Basic mock responses are useful for quick unit tests, but they often miss the conditions applications face in production. APIs may paginate results, respond slowly, or fail intermittently. By extending Jest mocks to replicate these real-world conditions, you can test whether the application behaves correctly under stress and complexity.

1. Mocking APIs That Return Paginated Data

Many APIs return data in pages, with metadata such as page, limit, and totalPages. Applications must request each page in sequence and combine the results. Without testing pagination, it is easy to miss bugs such as infinite loops or duplicated data.

How to mock:

You can configure the mock to return different responses based on the page number passed in the request. Each response simulates one page of results.

				
					// Example: mocking a paginated user API
const mockUsersPage1 = { users: [{ id: 1 }, { id: 2 }], page: 1, totalPages: 2 };
const mockUsersPage2 = { users: [{ id: 3 }, { id: 4 }], page: 2, totalPages: 2 };

jest.mock("axios");
import axios from "axios";

axios.get.mockImplementation((url) => {
  if (url.includes("page=1")) {
    return Promise.resolve({ data: mockUsersPage1 });
  }
  if (url.includes("page=2")) {
    return Promise.resolve({ data: mockUsersPage2 });
  }
});
				
			

What this tests:

  • The client correctly makes sequential requests.
  • The results from both pages are merged without duplication.
  • Fetching stops when the final page is reached.

2. Simulating Network Delays and Timeouts

In production, APIs may take longer than expected. The application must display loaders, handle slow responses gracefully, and sometimes enforce timeouts.

How to mock:

You can simulate a delay by returning a promise that resolves after a set duration. With Jest’s fake timers, you can advance time in the test without waiting in real time.

				
					jest.useFakeTimers();

axios.get.mockImplementation(() => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ data: { message: "Success after delay" } });
    }, 3000); // 3 seconds delay
  });
});

// In the test
await act(async () => {
  jest.advanceTimersByTime(3000);
});
				
			

What this tests:

  • Loading spinners or placeholders are shown during delay.
  • The application does not freeze or hang.
  • Timeout handling works if the delay exceeds the configured threshold.

3. Conditional Mocks Based on Request Parameters

Most APIs behave differently depending on parameters like query filters or resource IDs. Hardcoding one response does not validate that the application can adapt.

How to mock:

Check the arguments passed to the mocked function and return different results accordingly.

				
					axios.get.mockImplementation((url) => {
  if (url.includes("status=active")) {
    return Promise.resolve({ data: [{ id: 1, status: "active" }] });
  }
  if (url.includes("status=inactive")) {
    return Promise.resolve({ data: [{ id: 2, status: "inactive" }] });
  }
  return Promise.resolve({ data: [] });
});
				
			

What this tests:

  • The client correctly constructs requests with filters.
  • Responses are parsed accurately depending on the query.
  • Edge cases like “no results found” are handled.

4. Simulating API Failures and Retry Logic

APIs fail in unpredictable ways: server errors, dropped connections, or bad data. A robust application should retry where needed, show clear errors, and not silently fail.

How to mock:

Configure the mock to fail on the first call, then succeed on retry.

				
					let callCount = 0;

axios.get.mockImplementation(() => {
  callCount++;
  if (callCount === 1) {
    return Promise.reject({ response: { status: 500 } }); // first call fails
  }
  return Promise.resolve({ data: { message: "Success on retry" } });
});
				
			

What this tests:

  • Retry logic is triggered after a failure.
  • The correct number of retries occurs before giving up.
  • Error messages are shown when retries are exhausted.

5. Testing Multi-Step API Workflows With Dependent Calls

Some workflows depend on chained calls. For example, creating a resource may return an ID, which is then used in a second request. If either call fails, the workflow breaks.

How to mock:

Return different responses depending on which endpoint is called, while ensuring the second mock uses data from the first.

				
					axios.post.mockImplementation((url) => {
  if (url.endsWith("/create")) {
    return Promise.resolve({ data: { id: 101 } }); // step 1: create resource
  }
});

axios.get.mockImplementation((url) => {
  if (url.includes("/details/101")) {
    return Promise.resolve({ data: { id: 101, name: "Test Resource" } }); // step 2: fetch details
  }
});
				
			

What this tests:

  • Data flows correctly from one call to the next.
  • Errors in any step are caught and handled.
  • The final state reflects all dependent calls.

Best Practices for Mocking API Calls in Jest Tests

Mocking API calls can solve many testing challenges, but if not handled carefully, it can also create brittle tests and unnecessary maintenance work. Below are the key practices that both developers and testers should follow.

  • Use realistic data: Mirror the structure of real API responses, including nested objects, arrays, and status codes.
  • Organize mock data separately: Keep mock responses in dedicated files or fixtures so tests remain clean and reusable.
  • Cover both success and failure paths: Write tests for 200 OK, 400/500 errors, empty responses, and timeouts.
  • Add dynamic behavior when needed: Return different responses based on request parameters or call order to mimic real conditions.
  • Document mocks clearly: Add comments or use naming conventions that explain what each mock is designed to test.
  • Avoid over-mocking: Mock APIs only when necessary and use live calls in integration tests for end-to-end coverage.
  • Sync mocks with the real API: Regularly update mock structures using contracts, schemas, or API documentation to prevent drift.

How Requestly Helps with Mock API Calls in Jest

Jest provides powerful tools for mocking APIs, but maintaining test data and ensuring it matches real-world scenarios can become a challenge. Requestly complements Jest by acting as an external mock server and traffic modifier, which gives developers and testers more control over how API calls behave during tests.

Here are some core features of Requestly that help mock API calls in Jest.

  • Create mock endpoints: Define custom endpoints to replace real APIs and use them directly in Jest tests.
  • Modify API responses: Return tailored payloads for different test cases, including edge scenarios.
  • Change HTTP status codes: Simulate success, client errors, or server failures to verify application handling.
  • Add request delays: Reproduce slow or unstable networks to test loading states and timeouts.
  • Override GraphQL responses: Mock queries and mutations so Jest tests can cover GraphQL workflows and REST.

Conclusion

Mocking API calls in Jest helps create reliable and controlled test environments. By simulating different scenarios such as pagination, failures, retries, and network delays, tests become closer to real-world behavior. Requestly adds further flexibility by enabling developers to define mock endpoints, modify responses, and replicate conditions that are difficult to reproduce manually.

Written by
Rohit Rajpal
Rohit Rajpal is a B2B Content Strategist at BrowserStack, helping SaaS brands build AI-first strategies that drive authority and revenue. He writes about content, strategy, and the future of search in the zero-click era.

Related posts