Мокирование в JavaScript стало возможным благодаря одноэлементной природе функции require. Когда модуль требуется, он кэшируется в объекте require.cache, который содержит сопоставление путей к файлам с объектами Module. Последующие попытки запросить тот же модуль из того же пути к файлу не будут перезагружать модуль, а вместо этого вернут ранее загруженный объект Module. Подробнее см. в разделе кэширование.

Также обратите внимание на предостережения

Чтобы увидеть содержимое require.cache, рассмотрим следующие два файла:

util.js

const http = require('http');

const makeHttpGetCall = url => {
  return http.get(url);
};

module.exports = { makeHttpGetCall };

index.js

const util = require('./util');

console.log(require.cache);

Запуск node index.js выведет следующий объект:

{
  '/path/to/index.js': Module {...},
  '/path/to/util.js': Module {...}
}

Это показывает, что объекты Module для index.js и util.js были кэшированы в require.cache. Пока требуются одни и те же файлы по одним и тем же путям, будут возвращены одни и те же объекты Module.

Теперь давайте изменим index.js следующим образом:

const util = require('./util');
const http = require('http'); // same instance of http module as in util.js

// mock http.get function completely
http.get = () => {
  return new Promise((resolve, reject) => {
    resolve(200);
  });
};

util.makeHttpGetCall('some url').then(response => {
  console.log(response);
});

В этом примере мы по-прежнему получаем тот же экземпляр модуля http, что и в util.js, и любые изменения, внесенные в объект http, также будут отражены в util.js. Смоделировав функцию http.get и предоставив собственную реализацию, мы можем контролировать поведение функции makeHttpGetCall в util.js.

По сути, именно так работает насмешка в JavaScript с использованием модулей CommonJS. Используя одноэлементную природу require и возможность изменять объекты в кеше, мы можем имитировать