Ответ
Mock (мок) — это объект-заглушка, который имитирует поведение реальных объектов в контролируемых условиях. Моки являются ключевым инструментом для изоляции тестируемого кода от внешних зависимостей (например, баз данных, сетевых API, файловой системы).
Основная библиотека для мокирования в Python — это unittest.mock
, которая отлично интегрируется с pytest
.
Зачем нужны моки?
- Изоляция: Тестировать один компонент системы, не затрагивая другие.
- Предсказуемость: Задавать конкретные возвращаемые значения или исключения, чтобы проверить все ветки кода.
- Скорость: Избегать медленных операций, таких как сетевые запросы или работа с диском.
- Проверка взаимодействий: Убедиться, что тестируемый код правильно вызывает методы зависимых объектов.
Пример использования с pytest
и pytest-mock
:
Плагин pytest-mock
предоставляет удобную фикстуру mocker
.
# a.py - модуль с функцией, которую мы хотим протестировать
import requests
def get_user_data(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
response.raise_for_status() # Вызовет исключение для плохих статусов
return response.json()
# test_a.py - тест для функции
import pytest
import requests
from .a import get_user_data
def test_get_user_data_success(mocker):
# 1. Создаем mock для ответа от requests.get
mock_response = mocker.Mock()
mock_response.json.return_value = {"id": 1, "name": "John Doe"}
mock_response.raise_for_status.return_value = None # Указываем, что метод не должен вызывать исключение
# 2. Подменяем (patch) метод requests.get
mocker.patch('requests.get', return_value=mock_response)
# 3. Вызываем нашу функцию
user_data = get_user_data(1)
# 4. Проверяем результат и то, что mock был вызван правильно
assert user_data == {"id": 1, "name": "John Doe"}
requests.get.assert_called_once_with("https://api.example.com/users/1")
def test_get_user_data_failure(mocker):
# Подменяем requests.get, чтобы он вызывал исключение
mocker.patch('requests.get', side_effect=requests.exceptions.HTTPError)
# Проверяем, что наша функция корректно обрабатывает исключение
with pytest.raises(requests.exceptions.HTTPError):
get_user_data(2)
В этом примере мы полностью контролируем поведение requests.get
, не делая реального сетевого запроса.