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

К какому уровня относятся тесты на моках

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

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

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

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

К какому уровню относятся тесты на моках?

Тесты с использованием моков (mock objects) или других видов тестовых дублеров (стабы, заглушки, фейки) в первую очередь относятся к уровню модульных тестов (Unit Tests). Это их основная и наиболее естественная область применения в классической тестовой пирамиде. Однако, их использование не ограничивается строго одним уровнем и может распространяться на интеграционные тесты, что требует отдельного рассмотрения и осторожности.

Тесты на моках как модульные тесты (Unit-уровень)

Основная цель моков — изолировать тестируемый модуль (чаще всего — отдельный класс или функцию) от его зависимостей. Это позволяет сосредоточиться на проверке именно внутренней логики этого модуля.

Характеристики таких тестов:

  • Объект тестирования: Один класс, функция или метод.
  • Изоляция: Все внешние зависимости (базы данных, файловые системы, сетевые сервисы, другие сложные классы) заменяются на контролируемые заглушки.
  • Скорость: Выполняются очень быстро, так как не требуют развёртывания инфраструктуры.
  • Детерминированность: Результат предсказуем, так как поведение моков задаётся явно в рамках теста.
  • Цель: Проверить, что бизнес-логика модуля корректна и что он правильно взаимодействует со своими зависимостями (например, вызывает определенные методы с нужными аргументами).

Пример модульного теста с Mock на Python (pytest + unittest.mock):

import pytest
from unittest.mock import Mock, create_autospec
from order_service import OrderProcessor
from payment_gateway import PaymentGateway
from inventory_service import InventoryService

def test_process_order_successfully_charges_and_updates_inventory():
    # 1. ARRANGE: Создаём моки всех зависимостей
    mock_payment_gateway = create_autospec(PaymentGateway)
    mock_inventory_service = create_autospec(InventoryService)

    # Настраиваем поведение моков
    mock_payment_gateway.charge.return_value = {"status": "success", "transaction_id": "txn_123"}
    mock_inventory_service.reduce_stock.return_value = True

    # Внедряем моки в тестируемый модуль
    order_processor = OrderProcessor(mock_payment_gateway, mock_inventory_service)
    test_order = {"item_id": "item_1", "quantity": 2, "price": 100}

    # 2. ACT: Выполняем действие
    result = order_processor.process(test_order)

    # 3. ASSERT: Проверяем логику и взаимодействия
    # Проверяем результат работы
    assert result["success"] is True
    # Проверяем, что collaboration-зависимости были вызваны корректно
    mock_payment_gateway.charge.assert_called_once_with(amount=200)  # 2 * 100
    mock_inventory_service.reduce_stock.assert_called_once_with(item_id="item_1", quantity=2)

Это классический модульный тест: изолированная проверка класса OrderProcessor.

Тесты на моках как интеграционные тесты (Integration-уровень)

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

Характеристики таких тестов:

  • Объект тестирования: Взаимодействие нескольких реальных классов/модулей между собой.
  • Частичная изоляция: Критические или медленные внешние сервисы (платежные шлюзы, сторонние API) заменяются моками, но внутренняя интеграция (например, сервис -> репозиторий -> база данных в памяти) тестируется на реальных компонентах.
  • Цель: Проверить корректность совместной работы компонентов. Например, убедиться, что Service корректно преобразует данные и передает их в Repository.

Пример гибридного подхода (интеграция с моками) на Java (JUnit + Mockito):

@ExtendWith(MockitoExtension.class)
class UserProfileServiceIntegrationTest {

    @InjectMocks
    private UserProfileService profileService; // Реальный сервис

    @Mock
    private ExternalAnalyticsService analyticsService; // Мок внешней зависимости

    @Autowired
    private UserRepository repository; // Реальный репозиторий, работающий с H2 in-memory DB

    @Test
    void updateProfile_savesToDbAndSendsAnalyticsEvent() {
        // Настраиваем мок внешнего сервиса
        doNothing().when(analyticsService).sendEvent(anyString());

        User user = new User("id123", "OldName");
        repository.save(user); // Сохраняем в реальную in-memory БД

        // Действие: использует реальный репозиторий и мок аналитики
        profileService.updateUserName("id123", "NewName");

        // Интеграционная проверка: данные действительно обновились в БД
        User updatedUser = repository.findById("id123");
        assertEquals("NewName", updatedUser.getName());

        // Проверка взаимодействия с внешним миром
        verify(analyticsService).sendEvent("user_updated");
    }
}

Это уже интеграционный тест, хотя и с использованием мока для внешнего сервиса.

Ключевые выводы и рекомендации

  • Первичный уровень: Модульные тесты — родная стихия для моков.
  • Расширенное использование: Интеграционные тесты могут выборочно применять моки для дорогих и нестабильных внешних зависимостей, чтобы сделать тесты быстрее и стабильнее, не теряя фокуса на интеграции внутренних компонентов.
  • Чего следует избегать:
    *   Использования моков для тестирования **своего же кода**, который находится слишком близко (это приводит к хрупким тестам, повторяющим реализацию).
    *   Чрезмерного применения моков в **сквозных (E2E) тестах** — их суть в проверке работы всей системы в реалистичных условиях.
  • Альтернативы: Для изоляции на модульном уровне часто предпочтительнее использовать стабы (Stubs) или фейки (Fakes) — более простые и пассивные дублеры, которые просто возвращают предопределённые данные, не проверяя взаимодействия. Моки же чаще используются для проверки как происходит взаимодействие (behavior verification).

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