Hone logo
Hone
Problems

Mocking API Calls with Jest and jest-mock-extended

Testing asynchronous code, especially that which involves external API calls, can be challenging. Mocking these external dependencies allows us to isolate the code we're testing and ensure predictable test outcomes. This challenge focuses on using Jest to mock API calls in a TypeScript application.

Problem Description

You are tasked with writing unit tests for a TypeScript function that fetches data from an external API. To make these tests reliable and fast, you need to mock the API calls. You will be using Jest and the jest-mock-extended library to achieve this.

The specific function you need to test will be responsible for fetching user data from a hypothetical /api/users/:id endpoint.

Key Requirements:

  1. Mock the Fetch API: You need to mock the global fetch function to control its behavior during tests.
  2. Simulate Successful Responses: For valid user IDs, the mocked fetch should return a simulated successful response with user data.
  3. Simulate Error Responses: For invalid or non-existent user IDs, the mocked fetch should simulate an error response (e.g., a 404 Not Found).
  4. Test Data Retrieval: Write tests that verify your function correctly retrieves and processes data from the mocked API.
  5. Test Error Handling: Write tests that verify your function handles API errors gracefully.
  6. Use jest-mock-extended: Leverage jest-mock-extended to create and manage your mock objects.

Expected Behavior:

  • When the fetchUser function is called with a valid user ID, it should return a Promise that resolves with the user data.
  • When the fetchUser function is called with an invalid user ID, it should reject with an appropriate error.

Examples

Let's assume we have a userService.ts file with the following function:

// userService.ts
interface User {
  id: number;
  name: string;
  email: string;
}

export async function fetchUser(userId: number): Promise<User> {
  const response = await fetch(`/api/users/${userId}`);
  if (!response.ok) {
    throw new Error(`Failed to fetch user: ${response.statusText}`);
  }
  const data: User = await response.json();
  return data;
}

Example 1: Successful User Fetch

Input:
- Call `fetchUser(1)`

Mocked `fetch` behavior:
- When called with `/api/users/1`, it returns a Promise resolving to a Response object.
- The Response object's `ok` property is `true`.
- The Response object's `json()` method returns `Promise.resolve({ id: 1, name: 'Alice', email: 'alice@example.com' })`.

Expected Output (from calling `fetchUser(1)`):
{ id: 1, name: 'Alice', email: 'alice@example.com' }

Example 2: User Not Found

Input:
- Call `fetchUser(99)`

Mocked `fetch` behavior:
- When called with `/api/users/99`, it returns a Promise resolving to a Response object.
- The Response object's `ok` property is `false`.
- The Response object's `statusText` property is 'Not Found'.

Expected Output (from calling `fetchUser(99)`):
An error is thrown with the message 'Failed to fetch user: Not Found'.

Example 3: Network Error

Input:
- Call `fetchUser(2)`

Mocked `fetch` behavior:
- When called with `/api/users/2`, the Promise returned by `fetch` is rejected with a `TypeError('Network error')`.

Expected Output (from calling `fetchUser(2)`):
An error is thrown. The exact error message might depend on how Jest/TypeScript handles network errors, but the test should verify that an error occurred.

Constraints

  • All code must be written in TypeScript.
  • You must use Jest as your testing framework.
  • You must use the jest-mock-extended library for mocking.
  • Your solution should reside in a file named userService.test.ts.
  • The mocked fetch calls should accurately reflect the behavior described in the examples.

Notes

  • Remember that fetch returns a Promise. Your mocks need to handle this asynchronous nature.
  • The Response object returned by fetch has methods like json(), ok, and properties like statusText. You'll need to mock these as well.
  • Consider how to reset mocks between tests to ensure isolation.
  • Think about how to assert that fetch was called with the correct URL.
Loading editor...
typescript