Module Mocking

Module mocking replaces an imported dependency with a controlled substitute so a test can isolate the unit under test and determine external behavior. In React testing, it is useful for boundaries such as API clients, browser integrations, routing, or other modules whose real behavior is irrelevant to the test.

A module mock changes what a module import resolves to during a test. Instead of executing the dependency's real implementation, the test can provide functions, values, or objects with predictable behavior.

With Jest, `jest.mock()` is commonly used to mock a module. With Vitest, `vi.mock()` provides a similar capability. The exact mocking behavior and timing rules depend on the test runner and module system, so tests should follow that runner's documented conventions.

For example, a React component might import `fetchUser` from an API module. A unit test can mock that module and make `fetchUser` resolve to a known user. This keeps the test focused on how the component renders or reacts to the dependency's result rather than testing the network layer at the same time.

Mocks should represent the dependency boundary the test actually needs to control. If the real dependency is deterministic, fast, and useful to exercise as part of the test, mocking it may add unnecessary complexity. Over-mocking can also make tests pass while hiding integration problems between the component and its real dependencies.

Key points

Interview questions

Why would you mock a module in a React test?

I would mock a module when the component depends on behavior that the test should control, such as an API client. The mock gives the test a deterministic result without making a real network request. That lets the test focus on the component's response to that result. I would still use integration tests where exercising the real dependency relationship is important.

How does `vi.mock()` differ conceptually from mocking a function directly?

`vi.mock()` operates at the module boundary, replacing the module that an import resolves to. A direct function mock targets a particular function value that the test already has access to. Module mocking is useful when the unit imports a dependency internally and the test needs to control that imported module. The exact mechanics, including hoisting and factory behavior, are specific to the test runner.

What problems can excessive module mocking cause?

Excessive mocking can make tests validate assumptions about mocked implementations instead of real integrations. A component may pass because every dependency was replaced even though the real modules do not work together. Mocks can also become coupled to implementation details, making refactoring harder. A balanced test suite uses unit tests for isolation and integration tests for important real interactions.

How would you keep module mocks isolated between tests?

I would reset or restore mock state according to the test runner's facilities and the suite's requirements. Resetting call history is different from resetting mock implementations, while restoring can return spies or replaced functions to their original behavior. I would choose the least broad cleanup that preserves test independence. I would also avoid shared mutable mock data that can leak state across tests.

How would you decide whether a React dependency should be mocked?

I would first identify what behavior the test is intended to verify and where the meaningful system boundary lies. I would mock dependencies whose real behavior is slow, nondeterministic, externally controlled, or outside the unit's responsibility. I would prefer real implementations for small, deterministic collaborators when including them improves confidence without making the test fragile. For important interactions between real modules, I would add integration coverage rather than relying only on mocks.