Hone logo
Hone
Problems

Mocking Functions with Jest's jest.fn() in TypeScript

Understanding how to mock functions is a fundamental skill in modern JavaScript/TypeScript development, especially for testing. jest.fn() is Jest's powerful tool for creating mock functions that allow you to track calls, control return values, and simulate different behaviors. This challenge will guide you through using jest.fn() to effectively mock dependencies in your TypeScript code.

Problem Description

You are tasked with testing a TypeScript function that depends on an external service. To isolate your unit of code for testing, you need to replace the external service's function with a mock. Specifically, you need to:

  1. Create a mock function using jest.fn().
  2. Configure the mock function to return specific values or throw errors when called.
  3. Assert that the mock function was called with the correct arguments and a certain number of times.

This is crucial for ensuring that your code behaves as expected regardless of the actual implementation of its dependencies, leading to more reliable and maintainable tests.

Examples

Example 1:

Let's say you have a userService with a getUserById method that you want to mock.

  • Code Under Test (Conceptual):

    // Assume this is in userService.ts
    export const userService = {
      getUserById: async (id: number): Promise<{ id: number; name: string }> => {
        // Actual implementation would fetch from a database or API
        throw new Error("Not implemented for testing");
      }
    };
    
    // Assume this is in main.ts
    import { userService } from './userService';
    
    export async function getUserName(userId: number): Promise<string> {
      const user = await userService.getUserById(userId);
      return user.name;
    }
    
  • Test Scenario: Mock userService.getUserById to return a predefined user object.

  • Input (for the test):

    • A mock for userService.getUserById configured to return { id: 1, name: 'Alice' } when called with 1.
    • The getUserName function is called with userId = 1.
  • Output (from assertions in the test):

    • The mock function userService.getUserById was called once with 1.
    • The getUserName function returns the string 'Alice'.

Example 2:

Testing error handling.

  • Code Under Test (Conceptual):

    // Assume this is in dataFetcher.ts
    export const dataFetcher = {
      fetchData: async (url: string): Promise<any> => {
        // Actual implementation might fetch from an API
        throw new Error("Network error");
      }
    };
    
    // Assume this is in processor.ts
    import { dataFetcher } from './dataFetcher';
    
    export async function processData(dataId: string): Promise<string> {
      try {
        const response = await dataFetcher.fetchData(`/api/data/${dataId}`);
        return `Processed: ${JSON.stringify(response)}`;
      } catch (error) {
        return `Error processing data: ${error.message}`;
      }
    }
    
  • Test Scenario: Mock dataFetcher.fetchData to throw an error.

  • Input (for the test):

    • A mock for dataFetcher.fetchData configured to throw an Error('Failed to fetch') when called with /api/data/abc.
    • The processData function is called with dataId = 'abc'.
  • Output (from assertions in the test):

    • The mock function dataFetcher.fetchData was called once with /api/data/abc.
    • The processData function returns the string 'Error processing data: Failed to fetch'.

Constraints

  • The solution must be written entirely in TypeScript.
  • You should use jest.fn() to create all mock functions.
  • When configuring mocks, you should use methods like .mockReturnValue(), .mockResolvedValue(), .mockRejectedValue(), and .mockImplementation() as appropriate.
  • You must use Jest's assertion methods (e.g., expect(mockFn).toHaveBeenCalledWith(), expect(mockFn).toHaveBeenCalledTimes()) to verify mock interactions.

Notes

  • Consider how to handle asynchronous operations when mocking. jest.fn() can be used with .mockResolvedValue() and .mockRejectedValue() for Promises.
  • Think about how you would inject your mock functions into the code that uses them. This often involves exporting functions that take dependencies as arguments or using Jest's module mocking features (though for this challenge, direct replacement via variable assignment within the test scope is sufficient for demonstration).
  • The goal is to practice the core mechanics of jest.fn() for mocking and asserting.
Loading editor...
typescript