Мокирование в 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
и возможность изменять объекты в кеше, мы можем имитировать