jest.mock функция основы

jest.mock — ключевой механизм Jest для изоляции тестируемого кода от внешних зависимостей. В контексте React Testing Library он используется для подмены модулей, которые:

  • выполняют сетевые запросы;
  • работают с браузерными API;
  • зависят от сложной бизнес-логики;
  • имеют побочные эффекты;
  • замедляют или усложняют тесты.

Главная идея — тестировать поведение компонента, а не корректность работы его зависимостей.


Базовый принцип работы jest.mock

Функция jest.mock перехватывает импорт модуля и заменяет его на mock-реализацию.

jest.mock('./api')

После этого любой импорт ./api внутри тестируемого файла или импортируемых модулей будет заменён на mock.

Если не указать реализацию явно, Jest автоматически создаст автоматический mock — объект с теми же ключами, но функциями-заглушками (jest.fn()).


Влияние jest.mock на импорт модулей

Важно понимать порядок выполнения:

  • jest.mock всплывает (hoisting) и выполняется до импортов
  • mock применяется ко всем импортам модуля в данном тестовом файле
jest.mock('./userService')
import { getUser } from './userService'

Даже если jest.mock написан ниже, Jest всё равно поднимет его вверх.


Явная mock-реализация модуля

Для полного контроля используется фабричная функция:

jest.mock('./userService', () => ({
  getUser: jest.fn(),
  updateUser: jest.fn()
}))

Теперь:

  • getUser и updateUser — mock-функции
  • можно управлять возвращаемыми значениями
  • можно проверять количество вызовов и аргументы

Управление поведением mock-функций

Наиболее распространённые методы:

getUser.mockReturnValue({ name: 'Alex' })
getUser.mockResolvedValue({ name: 'Alex' })
getUser.mockRejectedValue(new Error('Ошибка'))

Пример в контексте React Testing Library:

getUser.mockResolvedValue({ name: 'Alex' })

render(<Profile />)

expect(await screen.findByText('Alex')).toBeInTheDocument()

Mock асинхронных зависимостей

Асинхронные функции (fetch, axios, API-клиенты) всегда мокаются через mockResolvedValue или mockRejectedValue.

jest.mock('./api', () => ({
  fetchPosts: jest.fn()
}))

fetchPosts.mockResolvedValue([
  { id: 1, title: 'Post' }
])

Это позволяет:

  • не использовать fetch
  • не поднимать MSW для простых сценариев
  • избежать нестабильных тестов

Частичное мокирование модуля

Иногда требуется сохранить часть реальной реализации.

Используется jest.requireActual:

jest.mock('./utils', () => {
  const actual = jest.requireActual('./utils')
  return {
    ...actual,
    formatDate: jest.fn(() => '01.01.2024')
  }
})

Полезно, когда:

  • модуль большой
  • мокаются только отдельные функции
  • остальная логика не влияет на тест

Мокирование компонентов React

Компоненты часто подменяются упрощёнными заглушками.

jest.mock('./Header', () => () => <div>Header</div>)

Применяется для:

  • исключения сложной верстки
  • устранения побочных эффектов
  • ускорения тестов

В RTL тестируется не внутреннее устройство компонента, а его влияние на DOM.


Мокирование хуков

Частый сценарий — подмена кастомных хуков.

jest.mock('./useAuth', () => ({
  useAuth: jest.fn()
}))

Затем:

useAuth.mockReturnValue({
  isAuth: true,
  user: { name: 'Admin' }
})

Это позволяет тестировать компонент в разных состояниях без сложной подготовки окружения.


Mock браузерных API

Некоторые API отсутствуют в JSDOM или ведут себя иначе.

Примеры:

jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: jest.fn()
}))

Или:

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(() => ({
    matches: false,
    addListener: jest.fn(),
    removeListener: jest.fn()
  }))
})

Очистка mock-состояния между тестами

Mock-функции сохраняют историю вызовов.

Рекомендуемые практики:

afterEach(() => {
  jest.clearAllMocks()
})

Различия методов:

  • clearAllMocks — очищает историю вызовов
  • resetAllMocks — сбрасывает реализацию
  • restoreAllMocks — возвращает оригинальные функции (для spy)

Отличие jest.mock от jest.spyOn

jest.mock:

  • полностью заменяет модуль
  • работает на уровне импорта
  • используется для изоляции

jest.spyOn:

  • оборачивает реальную функцию
  • сохраняет исходную реализацию (по умолчанию)
  • удобен для точечных проверок
const spy = jest.spyOn(api, 'getUser')
spy.mockResolvedValue({ name: 'Alex' })

Распространённые ошибки при использовании jest.mock

Мок после импорта

import { getUser } from './api'
jest.mock('./api') // не сработает ожидаемо

Несоответствие путей

jest.mock('../api') // путь должен совпадать с import

Забытый сброс состояния

  • тесты начинают зависеть друг от друга
  • появляются нестабильные результаты

Роль jest.mock в философии React Testing Library

React Testing Library фокусируется на том, как пользователь взаимодействует с интерфейсом, а не на деталях реализации.

jest.mock в этой модели:

  • изолирует компонент от инфраструктуры
  • позволяет управлять состояниями
  • делает тесты предсказуемыми
  • ускоряет выполнение

Моки используются не ради мока, а ради проверки отображения, поведения и реакций интерфейса на разные сценарии.