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:
- Call
UserDataService.getUserData(userId)with the provideduserId. - If the data is successfully retrieved, format the user's name (capitalize the first letter of each name part).
- Return the formatted name.
- If
UserDataService.getUserDatathrows an error,processUserDatashould catch the error and return "Error fetching user data".
You need to write a Jest test that:
- Verifies that
processUserDatacallsUserDataService.getUserDatawith the correctuserId. - Verifies that when successful,
processUserDataformats the name correctly. - Verifies that when
UserDataService.getUserDatathrows an error,processUserDatareturns 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
UserDataServiceis defined as follows:
class UserDataService {
getUserData(userId: number): Promise<{ firstName: string; lastName: string }> {
throw new Error("Not implemented - this is a placeholder");
}
}
- The
processUserDatafunction 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 mockUserDataService. - Use
mockImplementationormockResolvedValueto define the behavior of your mock. - Use
mock.callsormock.mock.callsto 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
processUserDatais anasyncfunction, so you'll need toawaitthe result of your test.