Jest предоставляет мощный механизм мокирования (mocking) для изоляции
тестируемого кода и контроля поведения зависимостей. Мокирование — это
техника, позволяющая заменять реальную логику зависимых компонентов на
поддельную, что помогает избежать сложностей с тестированием
взаимодействий и внешних эффектов. В Jest мокирование осуществляется с
помощью функции jest.mock(). Эта функция предоставляет
простое и гибкое решение для тестирования кода, который зависит от
внешних модулей, таких как API, базы данных или сторонние
библиотеки.
Для мокирования модуля достаточно вызвать jest.mock() и
указать путь к модулю, который нужно замокать. Jest автоматически
заменит этот модуль на поддельную версию, позволяя контролировать его
поведение в тестах. Например:
jest.mock('axios'); // Мокируем модуль axios
При этом Jest автоматически создаст поддельную версию модуля
axios, которая будет использоваться в тестах. По умолчанию
все функции и методы замокированного модуля будут являться заглушками,
возвращающими undefined.
Модуль можно замокать не только целиком, но и его отдельные функции.
Для этого следует использовать функционал мокирования с возвратом
конкретных значений. Например, для замены функции axios.get
на поддельную, которая возвращает заранее заданный результат:
import axios from 'axios';
jest.mock('axios'); // Мокируем модуль axios
test('Должен возвращать данные с API', async () => {
axios.get.mockResolvedValue({ data: 'response' }); // Устанавливаем поведение для get
const response = await axios.get('https://api.example.com/data');
expect(response.data).toBe('response');
});
В данном примере используется метод mockResolvedValue(),
который настраивает мок для возвращения промиса, успешно разрешающегося
с заданным значением. Для ошибок можно использовать метод
mockRejectedValue():
axios.get.mockRejectedValue(new Error('Network Error'));
В случае, если необходимо кастомизировать поведение мока в
зависимости от входных данных, можно передать функцию в метод
mockImplementation(). Это позволит задавать сложные логики
для мокируемых функций. Например:
axios.get.mockImplementation((url) => {
if (url === 'https://api.example.com/data') {
return Promise.resolve({ data: 'response' });
} else {
return Promise.reject(new Error('Not Found'));
}
});
Такой подход позволяет более точно контролировать поведение мока в зависимости от различных условий и входных параметров.
Мокирование модулей с внешними зависимостями является неотъемлемой
частью тестирования. Часто при тестировании нужно изолировать код от
реальных API вызовов или работы с базами данных. В таких случаях вместо
использования реальных библиотек и сервисов, можно замокать их поведение
с помощью jest.mock().
Пример:
import fetchData from './fetchData';
import axios from 'axios';
jest.mock('axios'); // Мокируем axios
test('Должен правильно обрабатывать данные', async () => {
axios.get.mockResolvedValue({ data: { id: 1, name: 'John' } });
const result = await fetchData('https://api.example.com/user');
expect(result.id).toBe(1);
expect(result.name).toBe('John');
});
Здесь мы замокали модуль axios, который используется
внутри функции fetchData. В реальной ситуации эта функция
бы отправляла HTTP запрос через axios, но в тестах мы
контролируем поведение вызова.
В Jest также можно мокировать встроенные модули Node.js, такие как
fs, path, http и другие. Это
полезно, когда нужно тестировать код, взаимодействующий с файловой
системой или сетью.
Пример мокирования модуля fs:
jest.mock('fs'); // Мокируем модуль fs
import fs from 'fs';
test('Должен читать файл', () => {
fs.readFileSync.mockReturnValue('Hello World'); // Задаем поведение метода readFileSync
const data = fs.readFileSync('/path/to/file.txt', 'utf-8');
expect(data).toBe('Hello World');
});
В этом примере мы заменяем метод readFileSync() на
заглушку, которая возвращает строку 'Hello World' вместо
чтения реального файла.
Если необходимо мокировать только определённые части модуля, можно использовать подмодули. Jest позволяет замокать отдельные части модуля, оставив остальные функциональности без изменений. Пример:
jest.mock('axios', () => {
return {
get: jest.fn(),
post: jest.fn()
};
});
import axios from 'axios';
test('Должен вызвать post метод', () => {
axios.post.mockResolvedValue({ data: 'response' });
// В коде теста используем axios.post
expect(axios.post).toHaveBeenCalled();
});
В этом примере мы мокируем только методы get и
post из модуля axios, оставляя возможность
использовать другие части модуля без изменений.
Перезапуск моков между тестами. Jest сбрасывает
моки после каждого теста, если используется метод
jest.resetAllMocks() или jest.clearAllMocks().
Это позволяет избежать влияния одного теста на другой, обеспечивая
изоляцию.
Мокирование целых зависимостей. При мокировании
целых зависимостей, таких как API, важно помнить, что любые асинхронные
операции, такие как вызовы API, должны корректно обрабатываться с
помощью методов mockResolvedValue() и
mockRejectedValue().
Автоматическое мокирование. Для некоторых
модулей можно использовать автоматическое мокирование с помощью
jest.autoMockOff() и jest.autoMockOn(), чтобы
управлять поведением моков в глобальном контексте.
Мокирование не только функций. Jest также
поддерживает мокирование значений, объектов, классов и методов, что
расширяет возможности использования jest.mock() в
тестах.
Мокирование сторонних зависимостей может стать сложной задачей, особенно если код зависит от множества внешних сервисов или библиотек. В таких случаях можно использовать последовательное мокирование нескольких модулей или применение различных техник мокирования для получения ожидаемого поведения.
Пример сложного мока с несколькими зависимостями:
jest.mock('axios');
jest.mock('fs');
import axios from 'axios';
import fs from 'fs';
import fetchData from './fetchData';
test('Должен обрабатывать данные и записывать их в файл', async () => {
axios.get.mockResolvedValue({ data: 'response' });
fs.writeFileSync.mockImplementation(() => {});
const result = await fetchData('https://api.example.com/data');
fs.writeFileSync('/path/to/output.txt', result.data);
expect(axios.get).toHaveBeenCalledWith('https://api.example.com/data');
expect(fs.writeFileSync).toHaveBeenCalledWith('/path/to/output.txt', 'response');
});
В данном примере происходит мокирование как HTTP-запроса с помощью
axios, так и операции записи в файл с помощью
fs.writeFileSync. Это демонстрирует, как мокировать
несколько внешних зависимостей в одном тесте.
Мокирование модулей с использованием jest.mock() — это
мощный инструмент для тестирования кода в изоляции, позволяющий
контролировать поведение зависимостей, таких как сторонние библиотеки,
API или модули Node.js. Это важный аспект тестирования, который помогает
создавать более стабильные и быстрые тесты, не затрагивая реальные
внешние ресурсы.