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

Ответ

Мокирование (mocking) — это техника в юнит-тестировании, при которой реальные зависимости объекта (например, модули для работы с базой данных, внешние API, файловая система) заменяются на управляемые объекты-заглушки (моки).

Основная цель — изолировать тестируемый код (SUT - System Under Test), чтобы проверить его логику в отрыве от внешних систем.

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

  1. Предсказуемость: Мок всегда возвращает заранее определенные данные, что делает тест стабильным.
  2. Скорость: Тесты выполняются быстрее, так как нет реальных сетевых запросов или обращений к диску.
  3. Изоляция: Ошибки в зависимостях не влияют на результаты теста, проверяется только логика самого юнита.
  4. Тестирование крайних случаев: Легко имитировать ошибки (например, недоступность сети или пустой ответ от БД) и проверить, как код их обрабатывает.

Пример с unittest.mock в Python:

Допустим, у нас есть функция, которая получает данные о погоде с внешнего API.

import requests

def get_weather_description(city: str) -> str:
    try:
        response = requests.get(f"https://api.weather.com/{city}")
        response.raise_for_status() # Вызовет исключение для плохих статусов
        data = response.json()
        return data['weather']['description']
    except requests.RequestException:
        return "Weather data unavailable"

Чтобы протестировать эту функцию без реального HTTP-запроса, мы «мокируем» requests.get:

from unittest import TestCase
from unittest.mock import patch

# Тест
class WeatherTest(TestCase):
    @patch('__main__.requests.get') # Указываем путь к объекту для мокирования
    def test_get_weather_description_success(self, mock_get):
        # 1. Настраиваем поведение мока
        mock_response = mock_get.return_value
        mock_response.json.return_value = {"weather": {"description": "Sunny"}}
        mock_response.raise_for_status.return_value = None

        # 2. Вызываем тестируемую функцию
        description = get_weather_description("London")

        # 3. Проверяем результат и вызовы
        self.assertEqual(description, "Sunny")
        mock_get.assert_called_once_with("https://api.weather.com/London")