Hone logo
Hone
Problems

Testing with Test Doubles in Jest: Simulating Dependencies

Writing robust tests often requires isolating the unit under test from its dependencies. Test doubles – mocks, stubs, and spies – allow us to replace real dependencies with controlled substitutes, enabling focused and predictable testing. This challenge will guide you in creating and utilizing these test doubles within a Jest testing environment to effectively test a function that relies on an external service.

Problem Description

You are tasked with testing a function processUserData which retrieves user data from a UserDataService and then formats it. The UserDataService is an external dependency that you don't want to actually call during testing (e.g., to avoid hitting a real database or API). Your goal is to create a mock UserDataService to control the data returned and verify that processUserData interacts with the service as expected.

Specifically, processUserData should:

  1. Call UserDataService.getUserData(userId) with the provided userId.
  2. If the data is successfully retrieved, format the user's name (capitalize the first letter of each name part).
  3. Return the formatted name.
  4. If UserDataService.getUserData throws an error, processUserData should catch the error and return "Error fetching user data".

You need to write a Jest test that:

  • Verifies that processUserData calls UserDataService.getUserData with the correct userId.
  • Verifies that when successful, processUserData formats the name correctly.
  • Verifies that when UserDataService.getUserData throws an error, processUserData returns the expected error message.

Examples

Example 1:

Input: userId = 123, UserDataService.getUserData returns { firstName: "john", lastName: "doe" }
Output: "John Doe"
Explanation: processUserData calls getUserData with 123, retrieves the data, and formats the name.

Example 2:

Input: userId = 456, UserDataService.getUserData throws an error "User not found"
Output: "Error fetching user data"
Explanation: processUserData calls getUserData with 456, catches the error, and returns the error message.

Example 3:

Input: userId = 789, UserDataService.getUserData returns { firstName: "jane", lastName: "smith" }
Output: "Jane Smith"
Explanation: processUserData calls getUserData with 789, retrieves the data, and formats the name.

Constraints

  • You must use Jest for testing.
  • You must use mock functions provided by Jest.
  • The UserDataService is defined as follows:
class UserDataService {
  getUserData(userId: number): Promise<{ firstName: string; lastName: string }> {
    throw new Error("Not implemented - this is a placeholder");
  }
}
  • The processUserData function is defined as follows:
async function processUserData(userId: number, userService: UserDataService): Promise<string> {
  try {
    const userData = await userService.getUserData(userId);
    const formattedName = `${userData.firstName.charAt(0).toUpperCase() + userData.firstName.slice(1)} ${userData.lastName.charAt(0).toUpperCase() + userData.lastName.slice(1)}`;
    return formattedName;
  } catch (error) {
    return "Error fetching user data";
  }
}

Notes

  • Consider using jest.fn() to create your mock UserDataService.
  • Use mockImplementation or mockResolvedValue to define the behavior of your mock.
  • Use mock.calls or mock.mock.calls to verify that the function was called with the correct arguments.
  • Think about how to simulate both successful and error scenarios for the UserDataService.
  • Remember that processUserData is an async function, so you'll need to await the result of your test.
Loading editor...
typescript