При тестировании компонентов на React иногда возникает необходимость
контролировать поведение внешних модулей, особенно если они содержат
сложную логику, сетевые запросы или состояния, которые трудно
предсказать. Одним из способов добиться этого является прямое
манипулирование require.cache в Node.js. Этот подход
позволяет «сбрасывать» или заменять модули во время выполнения теста,
что дает полный контроль над их поведением.
require.cacheNode.js кэширует загруженные модули в объекте
require.cache. Ключами этого объекта являются абсолютные
пути к файлам модулей, а значениями — сами объекты модулей. При
повторном вызове require('module') Node возвращает
кэшированную версию, вместо того чтобы загружать файл заново.
Ключевые моменты:
require.cache хранит объекты модулей,
включающие exports и внутренние свойства модуля.require.Для того чтобы сбросить модуль, необходимо получить его абсолютный
путь и удалить из require.cache:
const path = require('path');
function clearModuleCache(moduleName) {
const modulePath = require.resolve(moduleName);
delete require.cache[modulePath];
}
После вызова clearModuleCache('./myModule') следующий
require('./myModule') создаст новый экземпляр модуля.
Применение этого подхода в тестах позволяет:
Можно не только удалить модуль из кэша, но и сразу подменить его экспорт на мок-объект:
function mockModule(moduleName, mockImplementation) {
const modulePath = require.resolve(moduleName);
require.cache[modulePath] = {
id: modulePath,
filename: modulePath,
loaded: true,
exports: mockImplementation
};
}
Пример использования:
mockModule('./api', {
fetchData: jest.fn().mockResolvedValue({ id: 1, name: 'Test' })
});
const api = require('./api');
api.fetchData().then(data => console.log(data));
// { id: 1, name: 'Test' }
Здесь api.fetchData больше не выполняет оригинальный
сетевой запрос, а возвращает заранее заданный результат.
При тестировании компонентов React, которые импортируют сторонние модули (например, API-клиенты, конфигурации или сервисы), важно гарантировать, что каждый тест использует чистый модуль.
Пример:
import { render, screen } from '@testing-library/react';
beforeEach(() => {
// Сбрасываем кэш модуля перед каждым тестом
clearModuleCache('./api');
});
test('компонент отображает данные из API', async () => {
// Подменяем модуль на мок
mockModule('./api', {
fetchData: jest.fn().mockResolvedValue({ title: 'Hello World' })
});
const { default: MyComponent } = require('./MyComponent');
render(<MyComponent />);
const element = await screen.findByText('Hello World');
expect(element).toBeInTheDocument();
});
Преимущества такого подхода:
./api создается заново для каждого теста.jest.mock,
что делает тесты более локализованными и
предсказуемыми.jest.mock, msw или
dependency injection.Манипулирование require.cache — мощный инструмент для
глубокого контроля над модульной системой в тестах React. Он
обеспечивает чистоту среды тестирования, возможность
точного мокирования и помогает справляться с модулями,
которые сложно тестировать стандартными методами.