В тестировании React-приложений часто возникает ситуация, когда
требуется заменить часть поведения модуля на мок, но при этом сохранить
остальные функции или компоненты в их реальном виде. Для таких случаев
Jest предоставляет функцию jest.requireActual, которая
позволяет загружать реальную реализацию модуля, даже
если она будет частично мокирована.
jest.requireActual(moduleName) возвращает
оригинальный модуль, как если бы никакого мока не было.
Это особенно полезно при комбинировании моков и реального поведения:
import * as utils from './utils';
jest.mock('./utils', () => {
const actualUtils = jest.requireActual('./utils');
return {
...actualUtils,
fetchData: jest.fn(() => Promise.resolve({ data: [] })),
};
});
В данном примере:
fetchData заменяется на мок с предопределённым
результатом.utils остаются неизменными и
работают как в реальном коде.В React иногда нужно протестировать компонент, который импортирует другой компонент, но при этом часть внутреннего поведения зависимого компонента должна оставаться реальной.
Пример с компонентами:
import React from 'react';
import { render, screen } from '@testing-library/react';
import ParentComponent from './ParentComponent';
import * as ChildModule from './ChildComponent';
jest.mock('./ChildComponent', () => {
const actualChild = jest.requireActual('./ChildComponent');
return {
...actualChild,
ChildComponent: (props) => (
<div data-testid="mocked-child">
Мокнутый дочерний компонент
</div>
),
};
});
test('ParentComponent рендерит частично замокнутый ChildComponent', () => {
render(<ParentComponent />);
expect(screen.getByTestId('mocked-child')).toBeInTheDocument();
});
Здесь:
ChildComponent импортируется через
jest.requireActual.ChildComponent) заменяется
на кастомный мок.React Testing LibraryИспользование jest.requireActual в связке с RTL
позволяет создавать гибкие тесты, которые:
Пример интеграционного теста:
import React from 'react';
import { render, screen } from '@testing-library/react';
import Dashboard from './Dashboard';
import * as api from './api';
jest.mock('./api', () => {
const actualApi = jest.requireActual('./api');
return {
...actualApi,
getUserStats: jest.fn(() => Promise.resolve({ visits: 42 })),
};
});
test('Dashboard отображает реальные компоненты и замоканные данные', async () => {
render(<Dashboard />);
expect(await screen.findByText(/42 визитов/)).toBeInTheDocument();
});
Преимущества такого подхода:
Циклические зависимости Использование
jest.requireActual при циклических импортов может привести
к неожиданным результатам, так как Jest загружает модуль в момент вызова
функции. В таких случаях важно проверять порядок инициализации
моков.
Частичный мок по умолчанию Если модуль
экспортирует объект по умолчанию (default export), доступ к
нему через jest.requireActual нужно получать следующим
образом:
const actualModule = jest.requireActual('./module').default;Смешение моков и реальных функций При
объединении мока и реальных функций через spread-оператор
(...) необходимо следить, чтобы имена экспортов совпадали.
В противном случае тест может использовать неверную реализацию.
jest.requireActual только для модулей, где
необходимо частичное сохранение реального поведения.jest.requireActual превращает модули в гибкий инструмент
для тестирования, обеспечивая баланс между моками и реальной логикой.
Такой подход делает тесты более точными, понятными и
поддерживаемыми.