Jest предоставляет мощный механизм для тестирования JavaScript-программ, включая возможность создания моков (mock-объектов), которые помогают изолировать код, заменяя реальные зависимости заглушками. Это позволяет протестировать отдельные компоненты без необходимости взаимодействовать с внешними системами, такими как API или базы данных. Однако при использовании моков необходимо следить за их очисткой и сбросом между тестами, чтобы избежать побочных эффектов и обеспечить корректность тестов.
При написании тестов важно, чтобы они не зависели друг от друга, то есть каждый тест должен быть изолирован. Моки и шпионы, созданные в одном тесте, могут оставить изменения в глобальном состоянии, что повлияет на другие тесты. Например, если в одном тесте были изменены данные в мокированном объекте или функции, то эти изменения могут передаться в следующий тест, нарушив его корректность.
Jest предоставляет несколько механизмов для очистки моков:
jest.clearAllMocks(), jest.resetAllMocks(), и
jest.restoreAllMocks(). Эти методы очищают моки и
восстанавливают их исходное состояние, но они выполняют разные
действия.
jest.clearAllMocks()Метод jest.clearAllMocks() сбрасывает все вызовы
мокированных функций, но не меняет их состояние. Это означает, что если
вы мокировали функцию с конкретной реализацией или значением, то она
останется в том же состоянии, но все вызовы будут удалены. Это полезно,
если вам нужно просто очистить счетчик вызовов функции, но оставить её
поведение неизменным.
Пример использования:
test('тест 1', () => {
const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();
});
test('тест 2', () => {
const mockFn = jest.fn();
jest.clearAllMocks();
mockFn();
expect(mockFn).toHaveBeenCalled();
});
В этом примере jest.clearAllMocks() очищает информацию о
предыдущих вызовах в тесте 2.
jest.resetAllMocks()Метод jest.resetAllMocks() очищает все вызовы моков и
сбрасывает их состояние, включая любые настройки, такие как реализации
или возвращаемые значения. Этот метод используется, когда необходимо не
только очистить счетчик вызовов, но и вернуть моки к их первоначальному
состоянию, как если бы они были созданы заново.
Пример использования:
test('тест 1', () => {
const mockFn = jest.fn().mockReturnValue(42);
expect(mockFn()).toBe(42);
});
test('тест 2', () => {
const mockFn = jest.fn();
jest.resetAllMocks();
expect(mockFn()).toBeUndefined(); // mockFn сброшен
});
Здесь в тесте 1 функция mockFn возвращает 42, а в тесте
2, после сброса моков, она возвращает undefined, как если
бы не была настроена.
jest.restoreAllMocks()Метод jest.restoreAllMocks() восстанавливает
мокированные функции в их первоначальное состояние. Это полезно, если
были использованы такие методы, как jest.spyOn(), которые
подменяют оригинальные функции или методы.
jest.restoreAllMocks() возвращает эти методы в исходное
состояние, не затрагивая остальные моки.
Пример использования:
test('тест 1', () => {
const mockFn = jest.spyOn(console, 'log').mockImplementation(() => {});
console.log('test');
expect(console.log).toHaveBeenCalled();
});
test('тест 2', () => {
jest.restoreAllMocks();
console.log('test');
// Здесь будет вызван оригинальный console.log
});
В этом примере в тесте 1 подменяется console.log, а в
тесте 2, после вызова jest.restoreAllMocks(),
восстанавливается оригинальная реализация.
Использование beforeEach и
afterEach для очистки Для того чтобы избежать
побочных эффектов между тестами, рекомендуется вызывать методы очистки
моков в хуках beforeEach или afterEach. Это
позволит автоматически сбрасывать состояние моков перед или после
каждого теста, гарантируя, что каждый тест будет работать с чистым
состоянием.
Пример:
beforeEach(() => {
jest.clearAllMocks();
});
test('тест 1', () => {
const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();
});
test('тест 2', () => {
const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();
});
В этом примере мок очищается перед каждым тестом, что предотвращает влияние одного теста на другой.
Использование jest.clearAllMocks() в
глобальных настройках В некоторых случаях можно настроить
очистку моков глобально, чтобы не добавлять очистку в каждом тесте
вручную. Для этого можно использовать глобальные настройки Jest,
например, с помощью jest.setup.js:
// jest.setup.js
afterEach(() => {
jest.clearAllMocks();
});Мокирование в отдельных блоках Для того чтобы избежать необходимости очищать моки, лучше изолировать мокирование в отдельные блоки тестов. Таким образом, моки будут действовать только в пределах одного теста, не влияя на другие.
Пример:
describe('мокируем функцию API', () => {
let mockApi;
beforeEach(() => {
mockApi = jest.fn().mockReturnValue(Promise.resolve({ data: 'test' }));
});
test('тест 1', async () => {
const result = await mockApi();
expect(result.data).toBe('test');
});
});
describe('другие тесты', () => {
test('тест 2', () => {
// Здесь mockApi не определён
});
});
В этом примере мокируется функция API только в пределах первого блока тестов.
Очистка моков является неотъемлемой частью написания надежных и изолированных тестов в Jest. Выбор метода очистки зависит от требований к сбросу состояния моков: от простого очищения вызовов до полного сброса или восстановления оригинальных функций. Правильное использование этих методов и подходов помогает избежать побочных эффектов и обеспечивает стабильность тестов при работе с моками и шпионами.