Что такое mock/stub? Какие знаете инструменты для работы с ними?qa-143

1. Основные понятия

1.1 Stub

- **Определение**: Упрощенная реализация зависимости, возвращающая заранее заданные ответы
- **Когда использовать**:
  - Замена медленных сервисов
  - Эмуляция определенных состояний системы
  - Тестирование в изоляции
- **Пример**: Фиксированный ответ платежного шлюза "Успех"

1.2 Mock

- **Определение**: Умная заглушка с проверкой взаимодействий
- **Когда использовать**:
  - Проверка вызовов внешних сервисов
  - Верификация последовательности действий
  - Тестирование побочных эффектов
- **Пример**: Проверка что email-сервис был вызван ровно 1 раз

2. Ключевые различия

Характеристика Stub Mock
Цель Подмена зависимостей Проверка взаимодействий
Проверка вызовов Нет Да
Гибкость Статические данные Динамическое поведение
Сложность Простые Сложные
Использование Для данных Для поведения

3. Популярные инструменты

3.1 Универсальные библиотеки

- **Mockito** (Java):

// Пример создания mock UserService mockService = Mockito.mock(UserService.class); Mockito.when(mockService.getUser(1)).thenReturn(new User("test"));


- **Sinon.js** (JavaScript):

// Stub пример const stub = sinon.stub(userRepository, 'find').returns(Promise.resolve({id: 1}));


- **unittest.mock** (Python):

from unittest.mock import Mock mock = Mock(return_value=42)


### 3.2 Специализированные решения
  • WireMock (HTTP сервисы):

    stubFor(get(urlEqualTo("/api/resource"))
      .willReturn(aResponse()
      .withStatus(200)
      .withHeader("Content-Type", "application/json")
      .withBody("{'data': 'value'}")));
    
  • TestContainers (для баз данных):

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13");
    
  • Mountebank (Мультипротокольные моки):

    imposter = {
      protocol: 'http',
      port: 3000,
      stubs: [{
        responses: [{
          is: { statusCode: 200, body: { result: 'OK' } }
        }]
      }]
    };
    

4. Практические примеры использования

4.1 Тестирование сервиса оплаты

// Создаем mock платежного шлюза
PaymentGateway mockGateway = mock(PaymentGateway.class);

// Настраиваем ожидаемый вызов
when(mockGateway.process(any(Payment.class)))
  .thenReturn(SUCCESS);

// Проверяем поведение
orderService.processOrder(order);
verify(mockGateway, times(1)).process(any());

4.2 Тестирование с stub репозитория

// Создаем stub с тестовыми данными
UserRepository stubRepo = new UserRepositoryStub();
stubRepo.addUser(new User("test", "admin"));

// Внедряем stub в тестируемый сервис
AuthService authService = new AuthService(stubRepo);

// Проверяем логику
assertTrue(authService.login("test", "admin"));

5. Лучшие практики

5.1 Когда что использовать

✅ **Stub**:
  - Нужны только данные
  - Тестируемый код не вызывает внешние сервисы
  - Простые сценарии

✅ **Mock**:
  - Важно проверить взаимодействие
  - Тестируются побочные эффекты
  - Сложные сценарии с несколькими вызовами

5.2 Распространенные ошибки

❌ Слишком детальные проверки в mock (хрупкие тесты)
❌ Использование mock когда достаточно stub
❌ Игнорирование очистки состояния между тестами
❌ Мокирование того, что не принадлежит тесту

Резюмируем:

mocks и stubs — важнейшие инструменты для изолированного тестирования. Правильное их применение позволяет создавать быстрые, стабильные и содержательные тесты, не зависящие от внешних систем. Выбор между ними зависит от целей тестирования — проверка состояния (stub) или поведения (mock).