Для эффективного тестирования в JavaScript часто необходимо
изолировать компоненты и модули, с которыми они взаимодействуют. Одним
из способов достижения этой изоляции является подмена зависимостей, и в
этом контексте jest.mock() является мощным инструментом.
Этот метод позволяет создать заглушки для внешних модулей, чтобы
сосредоточиться на тестировании конкретных частей приложения без
зависимости от реальной реализации.
jest.mock()jest.mock() — это функция, предоставляемая Jest для
создания подмены для импортируемого модуля. Она позволяет заменить
поведение модуля в ходе выполнения тестов на фиктивное или на специально
подготовленное.
Когда вызывается jest.mock(), Jest автоматически
заменяет импортируемый модуль на объект с пустыми функциями или
значениями, которые можно настроить по необходимости.
Пример базового использования:
// math.js
export const sum = (a, b) => a + b;
export const multiply = (a, b) => a * b;
// test.js
import { sum, multiply } from './math';
jest.mock('./math', () => ({
sum: jest.fn().mockReturnValue(3),
multiply: jest.fn().mockReturnValue(5)
}));
test('mock sum function', () => {
expect(sum(1, 2)).toBe(3); // всегда возвращает 3
});
В этом примере jest.mock() используется для подмены
модуля math.js. Функции sum и
multiply заменяются на мок-версии, которые всегда
возвращают определенные значения, независимо от переданных аргументов.
Это позволяет тестировать код, не полагаясь на реальную реализацию этих
функций.
jest.mock()В некоторых случаях требуется изменить поведение мока в разных
тестах. Это можно сделать с помощью различных методов, предоставляемых
Jest, например, mockReturnValueOnce(), чтобы задать разные
возвращаемые значения для каждой конкретной итерации.
jest.mock('./math', () => ({
sum: jest.fn(),
multiply: jest.fn()
}));
test('mock sum function with different return values', () => {
sum.mockReturnValueOnce(3).mockReturnValueOnce(5);
expect(sum(1, 2)).toBe(3);
expect(sum(2, 3)).toBe(5);
});
Здесь mockReturnValueOnce() позволяет задать
возвращаемое значение для каждой конкретной вызова. Это полезно, когда
важно протестировать реакцию кода на различные результаты.
jest.mock() и
автоматическое восстановлениеJest позволяет автоматически восстанавливать поведение модуля между
тестами, чтобы каждый тест начинался с чистого состояния. Для этого
существует метод jest.restoreAllMocks(), который можно
вызывать после каждого теста или в глобальном блоке.
jest.mock('./math', () => ({
sum: jest.fn().mockReturnValue(3),
}));
afterEach(() => {
jest.restoreAllMocks();
});
test('test mock sum function', () => {
expect(sum(1, 2)).toBe(3);
// Модуль восстанавливается перед следующим тестом
});
restoreAllMocks() удаляет все моки, вернув модуль к его
изначальному состоянию. Это помогает избежать неожиданных побочных
эффектов между тестами.
Когда тестируемые модули используют классы или функции-конструкторы,
важно помнить, что jest.mock() будет подменять и их. Однако
для более сложных случаев, например, когда нужно заменить только методы
класса, можно использовать mockImplementationOnce() или
mockImplementation().
Пример использования с классом:
// user.js
export class User {
constructor(name) {
this.name = name;
}
greet() {
return `Hello, ${this.name}!`;
}
}
// test.js
import { User } from './user';
jest.mock('./user', () => {
return {
User: jest.fn().mockImplementation(() => {
return { greet: jest.fn().mockReturnValue('Mocked greeting!') };
})
};
});
test('test mocked class method', () => {
const user = new User('Alice');
expect(user.greet()).toBe('Mocked greeting!');
});
Здесь класс User заменяется на мок, а метод
greet() всегда возвращает заданную строку, что позволяет
изолировать тестируемый код от реальной реализации.
В асинхронных тестах также можно использовать
jest.mock() для подмены зависимостей, например, для замены
асинхронных запросов на мок-версии. Это позволяет проверить логику
приложения, не взаимодействуя с реальными сервисами или базами
данных.
import { fetchData } from './api';
jest.mock('./api');
test('mock fetchData in async test', async () => {
fetchData.mockResolvedValue({ data: 'Test data' });
const response = await fetchData();
expect(response.data).toBe('Test data');
});
Здесь метод fetchData подменяется на мок-версию, которая
возвращает промис с заданным результатом, что позволяет протестировать
асинхронное поведение без реальных сетевых запросов.
При работе с модулями, которые используют именованные экспорты,
jest.mock() также позволяет легко подменять эти экспорты.
Для этого можно указать точное имя экспортируемой функции или
объекта.
// utils.js
export const calculatePrice = (price, tax) => price + tax;
// test.js
import { calculatePrice } from './utils';
jest.mock('./utils', () => ({
calculatePrice: jest.fn().mockReturnValue(100)
}));
test('mock calculatePrice function', () => {
expect(calculatePrice(50, 10)).toBe(100);
});
В этом примере jest.mock() заменяет только функцию
calculatePrice, оставив другие функции модуля без
изменений.
Модули могут экспортировать как объекты, так и функции. Важно учитывать, что для подмены таких модулей может понадобиться немного больше усилий, так как нужно заменить не только методы, но и свойства объекта.
// logger.js
export const logger = {
info: (message) => console.log(message),
error: (message) => console.error(message)
};
// test.js
import { logger } from './logger';
jest.mock('./logger', () => ({
logger: {
info: jest.fn(),
error: jest.fn()
}
}));
test('mock logger methods', () => {
logger.info('Test info');
expect(logger.info).toHaveBeenCalledWith('Test info');
});
В случае с объектами мокирование предполагает замену всех методов объекта, что может быть полезно, когда необходимо подменить конкретные аспекты работы модуля, но оставить остальные без изменений.
jest.mock() с внешними библиотекамиИногда бывает необходимо подменить поведение внешних библиотек.
Например, если приложение использует стороннюю библиотеку для работы с
датами, такую как moment.js, то можно использовать
jest.mock() для замены всех вызовов на фиктивные
данные.
// date.js
import moment from 'moment';
export const getFormattedDate = () => moment().format('YYYY-MM-DD');
// test.js
import { getFormattedDate } from './date';
import moment from 'moment';
jest.mock('moment', () => jest.fn().mockReturnValue({
format: jest.fn().mockReturnValue('2023-01-01')
}));
test('mock moment.js', () => {
expect(getFormattedDate()).toBe('2023-01-01');
});
Здесь moment подменяется на мок, который всегда
возвращает заданную строку даты, что позволяет тестировать функции без
зависимости от реальной работы библиотеки.
Использование jest.mock() в тестировании является мощным
инструментом для изоляции тестируемого кода и контроля за зависимостями.
Благодаря гибкости этого метода можно создавать как простые, так и
сложные моки, которые позволяют сфокусироваться на тестировании
бизнес-логики, исключая внешние влияния.