Hone logo
Hone
Problems

Mocking require Cache for Consistent Jest Tests

Jest's default behavior can sometimes lead to inconsistent test results when modules rely on require for their initialization or when modules are dynamically loaded. This challenge asks you to create a mock require function that caches module resolutions, ensuring that subsequent require calls for the same module return the same resolved path, leading to more predictable and reliable tests. This is particularly useful when testing modules that perform side effects during initialization or when dealing with dynamic imports.

Problem Description

You need to implement a function createRequireCache that returns an object with a require property. This require property should behave like the standard Node.js require function, but with the added functionality of caching module resolutions. The first time require is called with a specific module ID, it should resolve the module path (as if using the standard require) and store it in an internal cache. Subsequent calls with the same module ID should return the previously resolved path from the cache, bypassing the actual module resolution process.

Key Requirements:

  • Caching: The require function must cache module resolutions.
  • Resolution: The first require call for a module should resolve the module path correctly.
  • Consistency: Subsequent require calls for the same module should return the cached path.
  • Mocking: The solution should be designed to be easily mocked or replaced in a Jest testing environment.
  • No Side Effects: The mocked require should not modify the actual Node.js require function.

Expected Behavior:

  1. When createRequireCache is called, it returns an object with a require property.
  2. The first time require(moduleId) is called, it should resolve the module path (simulating standard require behavior) and store the path in the cache.
  3. Subsequent calls to require(moduleId) should return the cached path immediately, without re-resolving the module.
  4. The cache should be internal to the returned object and not accessible from outside.

Edge Cases to Consider:

  • Module IDs that are not strings or numbers.
  • Circular dependencies (although the caching mechanism should prevent infinite loops, it's good to be aware of this).
  • The behavior of require when the module cannot be found (should ideally mimic the standard require behavior - throw an error).

Examples

Example 1:

Input:
const requireCache = createRequireCache();
requireCache.require('moduleA'); // moduleA resolves to './moduleA'
requireCache.require('moduleA'); // Should return './moduleA' immediately

Output:

'./moduleA'
'./moduleA'

Explanation: The first require('moduleA') resolves to './moduleA' and caches it. The second call returns the cached path without re-resolving.

Example 2:

Input:
const requireCache = createRequireCache();
requireCache.require('moduleB'); // moduleB resolves to './moduleB'
requireCache.require('moduleC'); // moduleC resolves to './moduleC'
requireCache.require('moduleB'); // Should return './moduleB' immediately

Output:

'./moduleB'
'./moduleC'
'./moduleB'

Explanation: Each require call resolves and caches the module path. Subsequent calls return the cached path.

Example 3: (Edge Case - Module Not Found)

Input:
const requireCache = createRequireCache();
requireCache.require('nonExistentModule');

Output:

Error: Cannot find module './nonExistentModule'

Explanation: The mocked require should throw an error if the module cannot be found, mimicking the standard require behavior.

Constraints

  • The solution must be written in TypeScript.
  • The createRequireCache function should have a time complexity of O(1) for subsequent require calls after the initial resolution.
  • The solution should not rely on external libraries beyond the standard Node.js modules.
  • The mocked require function should not modify the actual Node.js require function.

Notes

  • You can simulate module resolution by simply returning a predefined string based on the module ID. The focus is on the caching mechanism, not the actual module loading.
  • Consider using a Map or a plain JavaScript object to implement the cache.
  • Think about how to handle errors that might occur during module resolution (e.g., module not found). Mimic the standard require behavior.
  • This is a great exercise in understanding closures and how to create a private cache within a function.
Loading editor...
typescript