Чем отличается тестирование микросервисной архитектуры от тестирования монолита?

«Чем отличается тестирование микросервисной архитектуры от тестирования монолита?» — вопрос из категории Архитектура, который задают на 10% собеседований QA Тестировщик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключевое отличие — в распределенной природе микросервисов, что смещает фокус тестирования с модулей на взаимодействие между сервисами.

Аспект Монолит Микросервисы
Основной фокус Модульное и интеграционное тестирование внутри одной кодовой базы. Интеграционное и контрактное тестирование между независимыми сервисами.
Сложность изоляции Низкая. Зависимости — это другие классы/модули в том же процессе. Высокая. Сервисы работают в отдельных процессах, часто на разных хостах. Требуются моки и стабы.
Тестирование отказоустойчивости Обычно не является приоритетом на уровне приложения. Критически важно. Необходимо проверять поведение при падении зависимых сервисов (circuit breakers, retry-логика).
Конфигурация и развертывание Одно окружение, один конфиг. Множество окружений и конфигураций для каждого сервиса. Тестирование конфигов становится отдельной задачей.
Данные для тестов Общая база данных. У каждого сервиса может быть своя БД. Важно управлять тестовыми данными в распределенной транзакции.

Практические примеры для микросервисов:

  1. Использование стабов для изоляции (Python с unittest.mock):

    # test_order_service.py
    from unittest.mock import Mock, patch
    from payment_client import PaymentClient
    
    def test_create_order_with_successful_payment():
        # Создаем мок клиента платежного сервиса
        mock_payment_client = Mock(spec=PaymentClient)
        mock_payment_client.charge.return_value = {'status': 'success', 'transaction_id': 'tx_123'}
    
        # Внедряем мок в тестируемый сервис заказов
        order_service = OrderService(payment_client=mock_payment_client)
    
        result = order_service.create_order(user_id=1, amount=100)
    
        assert result['status'] == 'order_created'
        # Проверяем, что мок был вызван с правильными аргументами
        mock_payment_client.charge.assert_called_once_with(user_id=1, amount=100)
  2. Контрактное тестирование (Pact):

    • Потребитель (Consumer) определяет ожидаемый запрос и ответ от Поставщика (Provider).
    • Почему: Гарантирует, что изменения в API одного сервиса не сломают другого. Каждый сервис может тестироваться и развертываться независимо, имея уверенность в совместимости.
  3. Тестирование сетевых сбоев:

    • Использование инструментов вроде Chaos Engineering (например, Chaos Monkey) для симуляции задержек, таймаутов и недоступности сервисов в тестовом окружении.