Мокирование — важный элемент в процессе тестирования, позволяющий изолировать тестируемый код от внешних зависимостей. В контексте тестирования с использованием Jest мокирование компонентов становится неотъемлемой частью подхода к созданию эффективных и независимых тестов. В этой главе будет рассмотрено, как правильно использовать мокирование для тестирования компонентов в JavaScript с использованием Jest.
В процессе разработки часто встречаются ситуации, когда компонент зависит от внешних сервисов, API, или других модулей. Тестирование такого компонента без мокирования приведет к высокой вероятности ошибок, нестабильности тестов и излишней зависимости от внешних систем. Мокирование позволяет создать подмену этих зависимостей, что делает тесты более предсказуемыми, быстрыми и независимыми от реальных внешних сервисов.
Jest предоставляет несколько методов для мокирования различных типов зависимостей:
jest.fn() — для создания фиктивных
функций (мок-функций).jest.mock() — для замены модулей или
компонентов.jest.spyOn() — для шпионских функций,
которые отслеживают вызовы методов объекта.jest.fn()Функция jest.fn() позволяет создать мок-функцию, которую
можно использовать для проверки, был ли вызван тот или иной метод с
нужными аргументами. Например, если у нас есть функция, которая вызывает
метод компонента:
const myComponent = {
fetchData: () => 'data',
};
const mockFetchData = jest.fn(() => 'mocked data');
// Пример использования
myComponent.fetchData = mockFetchData;
Теперь, вместо оригинальной функции, будет вызываться мок-функция, и можно проверить, сколько раз и с какими аргументами она была вызвана:
test('fetchData is called', () => {
myComponent.fetchData();
expect(mockFetchData).toHaveBeenCalled();
});
jest.mock()Для более сложных зависимостей, таких как модули или библиотеки,
используется метод jest.mock(). Например, если компонент
зависит от библиотеки для работы с API, мы можем замокировать этот
модуль, чтобы избежать реальных запросов в процессе тестирования:
// Модуль для API
import api from './api';
// Мокирование модуля api
jest.mock('./api', () => ({
fetchData: jest.fn(() => Promise.resolve({ data: 'mocked data' })),
}));
// Тест компонента, использующего этот API
test('fetches data from api', async () => {
const data = await api.fetchData();
expect(data).toEqual({ data: 'mocked data' });
});
Здесь функция jest.mock() позволяет заменить реальный
вызов API на мокированную версию, которая возвращает заранее
подготовленные данные. Это исключает возможность случайных внешних
зависимостей в процессе тестирования.
jest.spyOn()Иногда требуется шпионить за вызовами методов уже существующих
объектов или классов. В этом случае используется метод
jest.spyOn(). Он позволяет отслеживать вызовы и аргументы
методов, не изменяя их поведение:
const myObject = {
doSomething: (value) => value * 2,
};
test('spying on doSomething method', () => {
const spy = jest.spyOn(myObject, 'doSomething');
myObject.doSomething(3);
expect(spy).toHaveBeenCalledWith(3);
spy.mockRestore(); // Восстановление оригинального метода
});
Метод jest.spyOn() не только шпионит за вызовами, но и
позволяет замокировать поведение метода с помощью
mockReturnValue или mockImplementation.
При тестировании React-компонентов часто возникает необходимость в
замещении внутренних зависимостей, таких как функции, API-запросы или
даже другие компоненты. Jest предоставляет возможности для мокирования
таких зависимостей, используя как описанные выше методы, так и
специальные утилиты, такие как jest.mock() для замены
компонент.
Если ваш компонент зависит от других компонентов, которые не влияют на логику тестируемого компонента, вы можете замокировать эти дочерние компоненты:
// Компонент Button, который будет замокирован
jest.mock('./Button', () => 'Button');
test('renders without crashing', () => {
const { container } = render(<MyComponent />);
expect(container).toMatchSnapshot();
});
В этом примере компонент Button заменяется строкой
'Button', что исключает его влияние на тестирование
компонента MyComponent.
Мокирование API-запросов в компонентах может быть выполнено с помощью
библиотеки jest-fetch-mock или стандартного мокирования с
jest.mock(). В случае использования стандартного
мокирования можно замокировать функции для имитации запросов:
import { fetchData } from './api';
// Мокирование fetch
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: 'mocked data' }),
})
);
test('fetches data from API', async () => {
const data = await fetchData();
expect(data).toEqual({ data: 'mocked data' });
});
Здесь мы замещаем глобальную функцию fetch, которая
обычно используется для отправки запросов, на мокированную версию,
возвращающую заранее подготовленные данные.
Для асинхронных операций важно помнить, что мок-функции и мокированные методы также поддерживают асинхронную работу. Мок-функции могут возвращать промисы, что позволяет правильно тестировать асинхронные компоненты и их поведение:
const asyncFunction = jest.fn(() => Promise.resolve('resolved value'));
test('tests async function', async () => {
const result = await asyncFunction();
expect(result).toBe('resolved value');
expect(asyncFunction).toHaveBeenCalled();
});
В данном примере asyncFunction — это мок, который
возвращает промис, и мы тестируем его поведение с использованием
async/await.
Jest позволяет управлять состоянием моков через методы восстановления
их исходного состояния или изменения поведения. Например, после
использования jest.spyOn() для отслеживания методов можно
восстановить исходное состояние метода:
const spy = jest.spyOn(myObject, 'doSomething');
spy.mockRestore(); // Восстановление оригинального метода
Так же можно очистить состояние всех моков после каждого теста с
помощью jest.clearAllMocks():
afterEach(() => {
jest.clearAllMocks();
});
Это необходимо для того, чтобы тесты не имели побочных эффектов между собой.
В классовых компонентах часто встречаются методы, которые зависят от внешних сервисов или API. Мокирование таких зависимостей помогает тестировать бизнес-логику компонента без необходимости реальных запросов.
class MyComponent extends React.Component {
componentDidMount() {
this.props.fetchData().then(data => this.setState({ data }));
}
render() {
return <div>{this.state.data}</div>;
}
}
test('fetches data on mount', async () => {
const fetchDataMock = jest.fn(() => Promise.resolve('mocked data'));
const wrapper = shallow(<MyComponent fetchData={fetchDataMock} />);
await wrapper.instance().componentDidMount();
expect(fetchDataMock).toHaveBeenCalled();
expect(wrapper.state().data).toBe('mocked data');
});
В этом примере мы замокировали проп fetchData, который
использует компонент для получения данных, и проверили, что данные были
правильно установлены в состояние компонента.
Мокирование в Jest — мощный инструмент для тестирования компонентов и
функций в изоляции. Используя методы jest.fn(),
jest.mock() и jest.spyOn(), можно эффективно
заменять зависимости и проверять поведение тестируемого кода. Это
позволяет создавать независимые, быстрые и стабильные тесты, которые не
зависят от внешних сервисов или сложных взаимодействий с другими частями
системы.