Hone logo
Hone
Problems

Jest Module Mocking: Isolating Dependencies for Robust Testing

Testing individual units of code in isolation is crucial for building reliable software. Module mocking in Jest allows us to achieve this by replacing real dependencies with controlled substitutes. This challenge will test your ability to effectively mock external modules and verify interactions within your TypeScript code.

Problem Description

Your task is to implement a robust module mocking system using Jest for a given TypeScript project. You will be provided with a simple application structure that includes a main module (main.ts) which depends on a utility module (utils.ts). Your goal is to write Jest tests for main.ts that mock the functionality of utils.ts. This will allow you to test main.ts independently, ensuring its logic is correct without relying on the actual implementation of utils.ts.

Key Requirements:

  1. Mock utils.ts: You need to mock the entire utils.ts module.
  2. Stub Functions: Specifically, you will need to stub (provide a fake implementation for) the formatMessage function exported from utils.ts.
  3. Spy on Calls: Assert that the formatMessage function was called with the expected arguments during the execution of a function in main.ts.
  4. Control Return Values: Configure the mocked formatMessage to return specific, predictable values for different inputs.
  5. Test main.ts Independently: Ensure that your tests for main.ts pass regardless of the actual implementation of utils.ts.

Expected Behavior:

Your tests should successfully mock utils.ts, control the behavior of formatMessage, and verify that main.ts uses the mocked function as expected.

Edge Cases:

  • Consider scenarios where the mocked function might not be called at all.
  • Handle cases where the mocked function is called multiple times with different arguments.

Examples

Let's assume the following simplified file structure:

src/utils.ts

export function formatMessage(message: string): string {
  // In a real scenario, this might do complex formatting.
  // For this challenge, its actual implementation doesn't matter for testing main.ts.
  console.log(`Actual formatMessage called with: ${message}`);
  return `Formatted: ${message}`;
}

src/main.ts

import { formatMessage } from './utils';

export function greetUser(name: string): string {
  const greeting = `Hello, ${name}!`;
  return formatMessage(greeting);
}

export function processData(data: string[]): string[] {
  return data.map(item => formatMessage(`Processing: ${item}`));
}

Example 1: Testing greetUser

  • Test Scenario: Mock formatMessage to return a specific string when greetUser calls it.
  • Input to main.ts function: greetUser("Alice")
  • Mock Configuration: Configure formatMessage to return "Mocked Greeting: Hello, Alice!"
  • Expected Output: "Mocked Greeting: Hello, Alice!"
  • Explanation: The greetUser function calls formatMessage with "Hello, Alice!". By mocking formatMessage, we intercept this call and return our predefined string, demonstrating that main.ts correctly utilizes the dependency.

Example 2: Testing processData and Spying

  • Test Scenario: Mock formatMessage and verify it's called for each item in the processData function.
  • Input to main.ts function: processData(["item1", "item2"])
  • Mock Configuration:
    • Mock formatMessage to return "Processed: ${originalMessage}" for any input.
    • Spy on calls to formatMessage.
  • Expected Output: ["Processed: Processing: item1", "Processed: Processing: item2"]
  • Explanation: The processData function maps over an array, calling formatMessage for each element. The test should assert that formatMessage was called twice, once for "Processing: item1" and once for "Processing: item2". The returned array should reflect the mocked return values.

Constraints

  • You must use Jest for your testing framework.
  • All code, including tests, must be written in TypeScript.
  • Assume the existence of src/utils.ts and src/main.ts as described in the examples.
  • Your tests should be placed in a src/__tests__/main.test.ts file.
  • You should not modify the original src/utils.ts or src/main.ts files for the purpose of writing tests.

Notes

  • Familiarize yourself with Jest's module mocking capabilities, particularly jest.mock() and jest.spyOn().
  • Consider how to define the mock implementation for formatMessage to ensure it behaves as expected for different inputs.
  • Think about how to assert the number of times a mocked function was called and with which arguments.
Loading editor...
typescript