Очистка и сброс моков

Jest предоставляет мощный механизм для тестирования JavaScript-программ, включая возможность создания моков (mock-объектов), которые помогают изолировать код, заменяя реальные зависимости заглушками. Это позволяет протестировать отдельные компоненты без необходимости взаимодействовать с внешними системами, такими как API или базы данных. Однако при использовании моков необходимо следить за их очисткой и сбросом между тестами, чтобы избежать побочных эффектов и обеспечить корректность тестов.

Зачем нужна очистка моков?

При написании тестов важно, чтобы они не зависели друг от друга, то есть каждый тест должен быть изолирован. Моки и шпионы, созданные в одном тесте, могут оставить изменения в глобальном состоянии, что повлияет на другие тесты. Например, если в одном тесте были изменены данные в мокированном объекте или функции, то эти изменения могут передаться в следующий тест, нарушив его корректность.

Методы очистки моков в Jest

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(), восстанавливается оригинальная реализация.

Лучшие практики для очистки моков

  1. Использование 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();
    });

    В этом примере мок очищается перед каждым тестом, что предотвращает влияние одного теста на другой.

  2. Использование jest.clearAllMocks() в глобальных настройках В некоторых случаях можно настроить очистку моков глобально, чтобы не добавлять очистку в каждом тесте вручную. Для этого можно использовать глобальные настройки Jest, например, с помощью jest.setup.js:

    // jest.setup.js
    afterEach(() => {
      jest.clearAllMocks();
    });
  3. Мокирование в отдельных блоках Для того чтобы избежать необходимости очищать моки, лучше изолировать мокирование в отдельные блоки тестов. Таким образом, моки будут действовать только в пределах одного теста, не влияя на другие.

    Пример:

    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. Выбор метода очистки зависит от требований к сбросу состояния моков: от простого очищения вызовов до полного сброса или восстановления оригинальных функций. Правильное использование этих методов и подходов помогает избежать побочных эффектов и обеспечивает стабильность тестов при работе с моками и шпионами.