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

Какие знаешь проблемы использования Mock объектов?

2.2 Middle🔥 201 комментариев
#Тестирование

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Проблемы использования Mock объектов

Mock объекты — мощный инструмент для unit тестирования, но их неправильное использование может привести к проблемам. Рассмотрю основные ловушки и как их избежать.

1. Тестирование деталей реализации вместо контракта

Самая частая ошибка — мокировать внутренние детали и проверять, как часто вызывалась функция. Это создает хрупкие тесты, которые ломаются при рефакторинге.

# ❌ Плохо: тест зависит от реализации
def test_user_registration(mock_email):
    service = UserService()
    service.register("user@example.com")
    # Этот тест сломается, если добавить кеширование
    assert mock_email.send.call_count == 1

# ✅ Хорошо: тест проверяет контракт
def test_user_registered_successfully(mock_email):
    service = UserService()
    service.register("user@example.com")
    # Проверяем, что email был отправлен хотя бы один раз
    assert mock_email.send.called

2. Over-mocking (чрезмерное мокирование)

Когда мокируешь слишком много, тест теряет ценность. Ты тестируешь не реальное поведение, а то, как ты себе его представляешь.

# ❌ Плохо: мокируем всё, включая логику
with patch('service.Database'):
    with patch('service.Logger'):
        with patch('service.Cache'):
            result = calculate(10)
            assert result == 20  # Не знаешь, работает ли реально

# ✅ Хорошо: используем реальные зависимости где возможно
def test_calculate_with_integration():
    # Используем реальную БД (или in-memory), мокируем только внешние API
    result = calculate(10)
    assert result == 20

3. Тесты не ловят настоящие баги

Если ты мокируешь неправильно, тест пройдет, но код упадет в продакшене.

# ❌ Плохо: mock возвращает строку, а реальный API возвращает объект
with patch('requests.get') as mock_get:
    mock_get.return_value = "success"  # Строка!
    response = fetch_user()
    print(response.json())  # AttributeError в продакшене!

# ✅ Хорошо: mock возвращает реалистичный объект
with patch('requests.get') as mock_get:
    mock_response = Mock()
    mock_response.json.return_value = {"id": 1, "name": "John"}
    mock_get.return_value = mock_response

4. Сложность в поддержке

Элабоичные mock setup усложняют тесты и делают их непонятными.

# ❌ Плохо: много строк для подготовки
def test_payment():
    with patch('stripe.Charge.create') as mock_charge:
        mock_charge.return_value = Mock(
            id="ch_123",
            amount=1000,
            status="succeeded",
            created=datetime.now()
        )
        # И только теперь начинается тест...
        result = process_payment(100)

5. Утечки памяти и побочные эффекты

Неправильно созданные mock'и могут оставить глобальное состояние в памяти.

# ❌ Плохо: патч не очищается
patch('module.global_config', {"debug": True})
def test_something():
    pass  # global_config остается в памяти!

# ✅ Хорошо: используй context manager
def test_something():
    with patch('module.global_config', {"debug": True}):
        pass  # Автоматически восстанавливается

6. Отсутствие side_effect

Мок не симулирует исключения и ошибки, которые могут произойти реально.

# ❌ Плохо: мок всегда успешен
with patch('requests.get') as mock:
    mock.return_value = Mock(status_code=200)
    # Не тестируем обработку ошибок!

# ✅ Хорошо: проверяем разные сценарии
with patch('requests.get') as mock:
    # Успешный случай
    mock.return_value = Mock(status_code=200, json=lambda: {"data": 1})
    assert fetch_data() == {"data": 1}
    
    # Сценарий ошибки
    mock.side_effect = requests.ConnectionError()
    with pytest.raises(FetchError):
        fetch_data()

Лучшие практики при работе с Mock'ами

1. Используй реальные объекты где возможно

  • In-memory БД вместо mock'ов
  • Фейковые API вместо patch
  • Тестовые контейнеры для интеграционных тестов

2. Mock'ируй границы системы

  • Внешние API
  • Слой доступа к данным (если используешь абстракцию)
  • Медленные операции

3. Проверяй поведение, а не реализацию

  • Интересует тебя что произойдет, а не как
  • Используй assert_called_once() только для критичных вызовов

4. Держи mock'и простыми

  • Не делай их слишком сложными
  • Лучше создать несколько тестов, чем один большой

5. Документируй intent

  • Почему ты мокируешь этот объект
  • Какое реальное поведение он симулирует

Правило 80/20

Мок объекты должны составлять не более 20% твоих тестов. 80% — это интеграционные тесты с реальными зависимостями. Это обеспечивает уверенность, что код действительно работает.