> Что такое mock и для чего он используется (Python)

Уровень: junior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: JetLend

Стек: Python

> Пример ответа

Короткий ответ

Mock - это объект-заглушка, который имитирует поведение реального объекта или функции в тестах. Он используется для изоляции тестируемого кода от внешних зависимостей: сетевых запросов, работы с базой данных, файловой системой или сторонними сервисами. Mock позволяет контролировать возвращаемые значения, проверять вызовы и аргументы, а также заменять медленные или нестабильные компоненты, делая тесты быстрыми, детерминированными и независимыми от окружения.

Подробное объяснение

В Python mock - это часть стандартной библиотеки unittest.mock. Основная идея: вместо реального объекта подставляется специальный объект, который записывает все обращения к себе и возвращает заранее заданные значения.

Зачем это нужно:

  • Изоляция теста - тестируем только логику конкретной функции или класса, не затрагивая внешние системы
  • Скорость - реальные вызовы API или БД могут занимать секунды, mock работает мгновенно
  • Детерминированность - результат теста не зависит от состояния внешней системы
  • Тестирование edge cases - можно имитировать ошибки, таймауты, редкие ответы сервера
  • Проверка взаимодействия - можно убедиться, что метод вызван с правильными аргументами и нужное количество раз

Основные инструменты в unittest.mock:

  • Mock - универсальный объект, у которого любые атрибуты и методы создаются на лету
  • MagicMock - подкласс Mock с поддержкой магических методов (__len__, __iter__ и т.д.)
  • patch - контекстный менеджер или декоратор для замены объектов в нужном месте
  • Mock.side_effect - позволяет задать исключение или последовательность возвращаемых значений
  • Mock.assert_called_with, assert_called_once, call_count - проверки вызовов

Важно понимать разницу между mock, stub и fake:

  • Stub - просто возвращает заранее заданные данные, не записывает вызовы
  • Fake - упрощённая рабочая реализация (например, in-memory БД)
  • Mock - записывает вызовы и позволяет проверять поведение

На практике

Типичный сценарий: функция get_user_data(user_id) делает HTTP-запрос к внешнему API. В тесте мы не хотим реально ходить в сеть - вместо этого подменяем requests.get на mock.

Где именно применять patch: важно патчить объект там, где он используется, а не там, где определён. Если функция делает from requests import get - патчим requests.get. Если делает import requests и вызывает requests.get - патчим requests.get в модуле requests.

Частая практика - использовать patch как декоратор или контекстный менеджер. Второй вариант удобнее, когда нужно передать mock в тестируемую функцию как аргумент или когда область патча ограничена.

Также стоит помнить про autospec=True в patch - это заставляет mock повторять сигнатуру реальной функции, что ловит ошибки в количестве аргументов.

Пример кода

PYTHON
from unittest.mock import Mock, patch
import requests
def get_user_name(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
data = response.json()
return data["name"]
# Тест с mock
@patch("requests.get")
def test_get_user_name(mock_get):
# Настраиваем mock
mock_response = Mock()
mock_response.json.return_value = {"name": "Alice"}
mock_get.return_value = mock_response
# Вызываем тестируемую функцию
result = get_user_name(42)
# Проверяем результат
assert result == "Alice"
# Проверяем, что запрос был сделан с правильным URL
mock_get.assert_called_once_with("https://api.example.com/users/42")

Пример с side_effect для имитации ошибки:

PYTHON
@patch("requests.get")
def test_get_user_name_error(mock_get):
mock_get.side_effect = requests.exceptions.Timeout
with pytest.raises(requests.exceptions.Timeout):
get_user_name(42)

Как отвечать на собеседовании

Начни с простого определения: mock - это замена реального объекта для изоляции теста. Затем перечисли основные сценарии использования: внешние API, БД, файловая система, нестабильные сервисы. Упомяни, что mock позволяет не только подменять поведение, но и проверять взаимодействие - сколько раз вызван метод, с какими аргументами.

Приведи конкретный пример из практики: например, тестирование функции, которая отправляет email. Вместо реальной отправки подставляем mock и проверяем, что функция вызвала send с правильными получателем и темой.

Если спросят про отличия от stub - объясни, что stub только возвращает данные, а mock дополнительно записывает вызовы и позволяет делать assertions.

Хорошо показать понимание patch и того, почему важно патчить в месте использования, а не определения.

Что проверяет интервьюер

  • Понимание назначения mock - изоляция тестов от внешних зависимостей
  • Знание конкретных инструментов Python: Mock, MagicMock, patch
  • Умение объяснить разницу между mock, stub, fake
  • Понимание, как проверять вызовы и аргументы
  • Осознание, что mock не должен использоваться для всего подряд - иногда лучше реальная интеграция

Типичные ошибки

  • Патч в неправильном месте - патчить модуль, где объект определён, вместо того, где он используется
  • Чрезмерное использование mock - мокать всё подряд, включая простые внутренние функции, что делает тесты хрупкими и бессмысленными
  • Забыть проверить вызовы - mock возвращает данные, но не проверяется, что метод вообще был вызван
  • Использовать Mock() вместо MagicMock() для объектов с магическими методами - например, при работе с контекстными менеджерами
  • Не использовать autospec - mock принимает любые аргументы, и ошибки в сигнатуре реальной функции не будут обнаружены
  • Путать return_value и side_effect - return_value задаёт результат, side_effect - исключение или последовательность значений
  • Мокать то, что не является внешней зависимостью - например, мокать собственную бизнес-логику, которую как раз и нужно тестировать

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью