Hone logo
Hone
Problems

Jest Provider Verification with Mocking

In modern software development, especially in microservice architectures, it's crucial to ensure that different services or modules interact correctly. This often involves mocking external dependencies to isolate the unit under test and verify its behavior. This challenge focuses on using Jest's mocking capabilities to verify that a specific function correctly interacts with a "provider" it depends on.

Problem Description

You are tasked with writing unit tests for a DataFetcher class. This class has a method fetchUserData that relies on an external UserProvider to retrieve user data. Your goal is to use Jest to mock the UserProvider and verify that DataFetcher.fetchUserData calls the UserProvider's getUserById method with the correct arguments and handles the returned data appropriately.

Key Requirements:

  1. Mock the Provider: Create a Jest mock for the UserProvider class.
  2. Stub Provider Method: Stub the getUserById method of the mocked UserProvider to control its return value.
  3. Verify Interaction: Assert that DataFetcher.fetchUserData calls UserProvider.getUserById exactly once with the expected user ID.
  4. Handle Success: Verify that when the provider returns user data, DataFetcher.fetchUserData correctly processes and returns this data.
  5. Handle Not Found: Verify that when the provider returns null (or undefined) indicating the user was not found, DataFetcher.fetchUserData returns null (or undefined).

Expected Behavior:

  • When fetchUserData is called with a valid user ID, it should call getUserById on the provider with that same ID.
  • If getUserById returns user data, fetchUserData should return that data.
  • If getUserById returns null or undefined, fetchUserData should also return null or undefined.

Examples

Let's assume the following interfaces for our classes:

interface User {
  id: string;
  name: string;
  email: string;
}

interface UserProvider {
  getUserById(id: string): Promise<User | null>;
}

class DataFetcher {
  constructor(private userProvider: UserProvider) {}

  async fetchUserData(userId: string): Promise<User | null> {
    const user = await this.userProvider.getUserById(userId);
    return user;
  }
}

Example 1: Successful Data Fetch

Input to Test:

  • userId: "user-123"
  • Mocked UserProvider.getUserById returns:
    { id: "user-123", name: "Alice Smith", email: "alice@example.com" }
    

Expected Output from fetchUserData:

{ id: "user-123", name: "Alice Smith", email: "alice@example.com" }

Explanation: The fetchUserData method is called with "user-123". The mocked UserProvider is configured to return a user object when getUserById is called with "user-123". DataFetcher then returns this mock user object.

Example 2: User Not Found

Input to Test:

  • userId: "user-456"
  • Mocked UserProvider.getUserById returns: null

Expected Output from fetchUserData:

null

Explanation: The fetchUserData method is called with "user-456". The mocked UserProvider is configured to return null for this ID, simulating a user not being found. DataFetcher correctly propagates this null return value.

Example 3: Provider Method Called Multiple Times (to ensure it's not)

Input to Test:

  • userId: "user-789"
  • Mocked UserProvider.getUserById returns: null
  • Implicitly, the test should ensure getUserById is called only once.

Expected Output from fetchUserData:

null

Explanation: Although the outcome is the same as Example 2, this scenario highlights the importance of verifying that the provider method is called precisely once, preventing accidental redundant calls.

Constraints

  • The solution must be written in TypeScript.
  • Jest must be used for mocking and assertions.
  • The UserProvider should be a mock.
  • The DataFetcher class should be tested in isolation.
  • All provider interactions should be asynchronous (using Promise).

Notes

  • Consider using jest.fn() to create mock functions and jest.spyOn() if you need to mock specific methods on an existing (though in this case, we'll mock the entire provider).
  • Pay close attention to the asynchronous nature of the provider methods. You'll need to use async/await in your tests.
  • Think about how you will set up the mock provider before each test to ensure test isolation.
  • The primary goal is to verify the interaction between DataFetcher and UserProvider, not the UserProvider's actual implementation (which is mocked).
Loading editor...
typescript