Jest предоставляет мощные инструменты для тестирования, и одной из ключевых возможностей является мокирование модулей. В обычных тестах могут возникать ситуации, когда нужно заменить часть модуля или только определенные функции, оставив другие без изменений. Это позволяет изолировать тестируемую логику от внешних зависимостей, улучшая точность тестов и упрощая их написание. Частичное мокирование модуля позволяет замещать только часть функционала модуля, оставляя остальной функционал нетронутым.
Когда разрабатывается приложение, многие его компоненты могут зависеть от внешних сервисов, библиотек или даже других частей приложения. Тестировать эти зависимости в интеграционных тестах или юнит-тестах не всегда целесообразно, особенно когда они содержат сложную логику или имеют побочные эффекты. Частичное мокирование помогает сконцентрироваться на важной части тестируемого кода, при этом не затрагивая другие функциональные элементы, которые не имеют отношения к текущему тесту.
В Jest можно частично замещать функции модуля с помощью
jest.mock() в сочетании с мок-функциями. Jest позволяет
изменять поведение только тех функций, которые необходимо заменить,
оставляя остальные функциональные части модуля работать в обычном
режиме. Это достигается использованием объекта, передаваемого в качестве
второго параметра jest.mock(), где определяются только те
методы, которые должны быть замещены.
Предположим, у нас есть модуль math.js, который содержит
несколько функций, и мы хотим замокировать только одну из них:
// math.js
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
const multiply = (a, b) => a * b;
module.exports = { add, subtract, multiply };
Теперь мы пишем тест для модуля, но нам нужно мокировать только
функцию add, чтобы протестировать, как работает другая
логика, не затрагивая результат сложения:
// math.test.js
jest.mock('./math', () => {
return {
add: jest.fn(() => 42), // Мокаем только функцию add
subtract: require.requireActual('./math').subtract, // Оставляем функцию subtract без изменений
multiply: require.requireActual('./math').multiply // Оставляем функцию multiply без изменений
};
});
const math = require('./math');
test('add function is mocked', () => {
expect(math.add(1, 2)).toBe(42); // Мокированное значение
});
test('subtract function works as usual', () => {
expect(math.subtract(5, 3)).toBe(2); // Реальное значение
});
В этом примере:
add, возвращающая значение
42, независимо от входных данных.subtract и multiply не тронуты и
будут использовать реальные реализации.jest.requireActualДля частичного мокирования часто используют функцию
jest.requireActual(), которая позволяет получить
оригинальную реализацию модуля. В сочетании с jest.mock()
это позволяет заменить только отдельные функции, оставляя остальную
часть модуля неизменной.
jest.requireActual() используется для того, чтобы
получить реальную версию модуля и затем изменить только его отдельные
части. Это полезно, когда необходимо использовать реальное поведение
некоторых функций в модулях, но при этом изолировать определенные части
для тестирования.
jest.mock('./math', () => {
const originalMath = jest.requireActual('./math'); // Получаем оригинальный модуль
return {
...originalMath, // Распаковываем все оригинальные функции
add: jest.fn(() => 100) // Мокаем только функцию add
};
});
const math = require('./math');
test('add function returns mocked value', () => {
expect(math.add(1, 2)).toBe(100); // Мокированное значение
});
test('multiply function uses real implementation', () => {
expect(math.multiply(3, 4)).toBe(12); // Реальная функция
});
Иногда бывает необходимо замокировать функции, которые импортируются и используются внутри других модулей. В таких случаях частичное мокирование можно применять для замены только тех частей, которые вызываются внутри тестируемого кода.
Предположим, у нас есть модуль, который использует внутреннюю функцию из другого модуля:
// external.js
const fetchData = () => {
// Например, вызываем внешний API
return 'real data';
};
module.exports = { fetchData };
// app.js
const { fetchData } = require('./external');
const processData = () => {
const data = fetchData();
return data.toUpperCase();
};
module.exports = { processData };
Чтобы замокировать только вызов fetchData, мы можем
использовать частичное мокирование:
// app.test.js
jest.mock('./external', () => {
return {
fetchData: jest.fn(() => 'mocked data') // Мокаем только fetchData
};
});
const { processData } = require('./app');
const { fetchData } = require('./external');
test('fetchData is mocked', () => {
expect(fetchData()).toBe('mocked data');
});
test('processData uses mocked fetchData', () => {
expect(processData()).toBe('MOCKED DATA');
});
Здесь мы замещаем только функцию fetchData, оставляя
логику обработки данных в processData неизменной.
Минимизация мокирования: Частичное мокирование следует применять только к тем функциям или модулям, которые напрямую влияют на логику теста. Мокирование всего модуля может привести к потерям в покрытии тестами и запутанности в коде.
Явное мокирование: Всегда старайтесь явно указывать, какие функции мокуются, и не полагайтесь на неявное поведение. Это помогает избежать неожиданных результатов, если мокирование будет случайно забыто.
Использование jest.restoreAllMocks:
Если после выполнения тестов необходимо восстановить оригинальные
значения моков, используйте jest.restoreAllMocks() для
возврата всех моков к их первоначальному состоянию.
Изоляция тестов: Частичное мокирование помогает изолировать тесты от внешних зависимостей, что улучшает точность и скорость тестов, но при этом не нужно забывать, что важно тестировать как взаимодействие с реальными зависимостями, так и поведение компонентов в рамках изолированных тестов.
Поддержка стабильности: Применение частичного мокирования помогает поддерживать стабильность тестов при изменении внешних зависимостей, таких как сторонние библиотеки или API, которые могут быть ненадежными или изменяться со временем.
Частичное мокирование — это мощный инструмент для тестирования, который позволяет изолировать отдельные части приложения, замещая только нужные функции, а не весь модуль. Использование таких техник помогает сделать тесты более гибкими, быстрыми и удобными, не теряя при этом точности.