Hone logo
Hone
Problems

Mocking External Dependencies with Jest Stubs

When building complex applications, your code often relies on external services, APIs, or other modules. Testing these components in isolation can be challenging because you don't want your tests to fail due to issues in these external dependencies. Jest stubs (or mocks) are a powerful technique to replace these dependencies with controlled, predictable versions, allowing you to focus solely on the logic of the code you're testing.

Problem Description

Your task is to write a Jest test suite for a hypothetical UserService class. This UserService class depends on an external DatabaseService to fetch and save user data. For testing purposes, you need to implement Jest stubs for the methods of the DatabaseService. This will allow you to test UserService's logic without actually interacting with a real database.

Specifically, you will need to:

  1. Create a mock DatabaseService: This mock will replace the actual DatabaseService.
  2. Stub specific methods: You'll need to stub the getUserById and saveUser methods of the DatabaseService.
  3. Control mock behavior: Configure your stubs to return specific values or throw errors to simulate different scenarios.
  4. Test UserService: Write tests that verify UserService correctly interacts with its mocked DatabaseService.

Examples

Example 1: Successful User Retrieval

Let's assume we have a UserService that has a getUserProfile method. This method calls DatabaseService.getUserById.

Input for the test:

  • DatabaseService.getUserById is stubbed to return a user object { id: 1, name: 'Alice' } when called with id = 1.

Expected behavior of UserService.getUserProfile(1):

The UserService.getUserProfile(1) method should return the user object { id: 1, name: 'Alice' }.

Explanation: The test verifies that UserService can successfully retrieve user data from the (mocked) database.

Example 2: User Not Found Scenario

Input for the test:

  • DatabaseService.getUserById is stubbed to return undefined (or throw a specific "not found" error) when called with id = 99.

Expected behavior of UserService.getUserProfile(99):

The UserService.getUserProfile(99) method should return undefined (or handle the "not found" error appropriately, e.g., by returning null or throwing a custom error).

Explanation: This test ensures that UserService handles cases where a user does not exist in the database.

Example 3: Successful User Saving

Input for the test:

  • A new user object { id: 2, name: 'Bob' } is passed to UserService.createUser.
  • DatabaseService.saveUser is stubbed to return true upon successful saving.

Expected behavior of UserService.createUser({ id: 2, name: 'Bob' }):

The UserService.createUser method should return true (or the result of the saveUser stub). The test should also assert that DatabaseService.saveUser was called exactly once with the correct user object.

Explanation: This test verifies that UserService correctly delegates the task of saving user data to the DatabaseService and checks if the save operation was successful.

Constraints

  • All provided code for UserService and DatabaseService will be in TypeScript.
  • Your tests must be written using Jest.
  • You should utilize Jest's mocking capabilities, specifically jest.fn() and mock implementations, to stub DatabaseService methods.
  • Tests should cover at least three distinct scenarios: successful retrieval, item not found, and successful saving.

Notes

Consider how you will handle the instantiation of UserService. It likely depends on an instance of DatabaseService. You'll need to inject your mock DatabaseService into UserService during testing. Think about different ways to achieve this (e.g., constructor injection).

The goal is to isolate the UserService logic and ensure it functions correctly, regardless of the actual state or availability of the DatabaseService.

Loading editor...
typescript