← Назад к вопросам

Как работал с мокированием?

2.0 Middle🔥 151 комментариев
#Теория тестирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой опыт работы с мокированием в автоматизации тестирования

Мокирование (Mocking) — это одна из ключевых техник изоляции тестируемого кода от его зависимостей, которую я активно применяю в проектах по автоматизации. В контексте QA Automation мокирование позволяет создавать контролируемое тестовое окружение, эмулируя поведение внешних систем, сервисов или сложных внутренних компонентов. Это делает тесты быстрыми, стабильными и независимыми от внешних факторов (как сетевые запросы, базы данных, сторонние API).

Основные цели и сценарии использования моков

  • Изоляция юнит-тестов: Проверка бизнес-логики приложения без необходимости разворачивать реальную БД или HTTP-сервисы.
  • Эмуляция внешних зависимостей: Замена реальных платежных шлюзов, email-сервисов, микросервисов или файловых систем.
  • Тестирование сложных или нештатных сценариев:
    *   Симуляция сетевых ошибок, таймаутов.
    *   Эмуляция возврата специфических данных (пустые ответы, некорректный JSON).
    *   Проверка поведения системы при недоступности сервиса.
  • Верификация взаимодействий (Interaction Testing): Проверка, что тестируемый код корректно вызывает методы зависимостей с ожидаемыми аргументами.

Ключевые инструменты и библиотеки в моей практике

В зависимости от стека технологий проекта я использовал следующие фреймворки:

  1. Для Java-проектов: Mockito — де-факто стандарт. Также применял EasyMock и WireMock для мокирования HTTP-сервисов.
  2. Для Python-проектов: unittest.mock (встроенная библиотека) и pytest-mock как удобная обертка.
  3. Для JavaScript/TypeScript: Jest (имеет мощную встроенную систему моков) и Sinon.JS.
  4. Универсальные: WireMock (для мокирования REST API на уровне HTTP) и MockServer.

Практический пример: мокирование HTTP-клиента в Python

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

# services/user_service.py (тестируемый код)
import requests

class UserService:
    def __init__(self, api_client):
        self.api_client = api_client

    def get_user_name(self, user_id):
        response = self.api_client.get(f'/users/{user_id}')
        if response.status_code == 200:
            return response.json().get('name')
        return None
# tests/test_user_service.py (тесты с использованием unittest.mock)
import pytest
from unittest.mock import Mock, patch
from services.user_service import UserService

def test_get_user_name_success():
    # 1. ARRANGE: Создаем мок-объект для api_client
    mock_api_client = Mock()

    # Настраиваем поведение мока: метод get возвращает объект с нужными атрибутами
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {'id': 1, 'name': 'Иван Петров'}
    mock_api_client.get.return_value = mock_response

    # Внедряем мок в тестируемый сервис (Dependency Injection)
    service = UserService(mock_api_client)

    # 2. ACT: Вызываем тестируемый метод
    result = service.get_user_name(user_id=1)

    # 3. ASSERT: Проверяем результат и взаимодействие
    assert result == 'Иван Петров'
    # Верификация: метод get был вызван ровно один раз с конкретным URL
    mock_api_client.get.assert_called_once_with('/users/1')

def test_get_user_name_failure():
    mock_api_client = Mock()
    mock_response = Mock()
    mock_response.status_code = 404
    mock_api_client.get.return_value = mock_response

    service = UserService(mock_api_client)
    result = service.get_user_name(999)

    assert result is None
    mock_api_client.get.assert_called_once_with('/users/999')

Контекстные менеджеры и патчинг (Patching)

Часто требуется временно подменить объект или функцию в модуле. Для этого используется patch.

# Пример патчинга через pytest-mock (фикстура mocker)
def test_get_user_name_with_patch(mocker):
    # Патчим функцию requests.get внутри модуля
    mock_get = mocker.patch('services.user_service.requests.get')
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {'name': 'Анна Сидорова'}
    mock_get.return_value = mock_response

    service = UserService(api_client=requests)
    result = service.get_user_name(5)

    assert result == 'Анна Сидорова'
    mock_get.assert_called_once_with('/users/5')

Важные принципы и best practices

  • Не мокировать то, что не принадлежит вам: Как правило, не следует мокировать код сторонних библиотек (кроме как для оберток), если это не является зависимостью.
  • Предпочитать Dependency Injection (DI): Конструкция, как в первом примере, делает код более тестируемым и явным, чем глобальный патчинг.
  • Проверять взаимодействия, но не злоупотреблять этим: Излишняя спецификация внутренних вызовов делает тесты хрупкими. Фокус должен быть на конечном результате.
  • Использовать моки для протоколов (интерфейсов), а не конкретных реализаций: Это повышает гибкость архитектуры.
  • Четкое именование мок-объектов: mock_user_repo, stub_http_client — для ясности.
  • Отдавать предпочтение Fakes или Stubs, если они достаточны: Моки (с верификацией вызовов) — более строгий и часто избыточный инструмент. Для простой подмены данных достаточно стабов (Stub).

В итоге, грамотное применение мокирования — это баланс между изоляцией тестов и их релевантностью реальному поведению системы. Это мощный инструмент в арсенале automation-инженера, который, однако, требует понимания принципов модульного тестирования и архитектуры приложения для эффективного и корректного применения.

Как работал с мокированием? | PrepBro