Как Dependency Injection (DI) улучшает тестируемость кода

Ответ

Dependency Injection (DI) кардинально улучшает тестируемость кода, так как позволяет подменять реальные зависимости тестовыми двойниками (моками, стабами).

Основная идея в том, что компонент не создаёт свои зависимости сам, а получает их извне. Это разрывает жёсткие связи между классами.

Преимущества для тестирования:

  • Изоляция: Можно тестировать один класс изолированно от его зависимостей.
  • Скорость: Тесты выполняются быстрее, так как не требуют подключения к реальным базам данных или отправки сетевых запросов.
  • Предсказуемость: Поведение моков полностью контролируется в тесте, что делает результаты стабильными.

Пример

Код без DI (сложно тестировать):

class PaymentGateway:
    def charge(self, amount: float):
        # Сложная логика с реальным API
        print(f"Charging {amount} via external API...")

class OrderProcessor:
    def process(self, order):
        # Зависимость создаётся внутри класса - это проблема
        payment_gateway = PaymentGateway()
        payment_gateway.charge(order.total)

Код с DI (легко тестировать):

class OrderProcessor:
    # Зависимость передаётся в конструктор
    def __init__(self, payment_gateway):
        self.payment_gateway = payment_gateway

    def process(self, order):
        self.payment_gateway.charge(order.total)

Пример теста с использованием мока:

from unittest.mock import Mock

def test_order_processor_charges_correct_amount():
    # 1. Создаём мок-объект для зависимости
    mock_gateway = Mock()

    # 2. Внедряем мок в тестируемый класс
    processor = OrderProcessor(payment_gateway=mock_gateway)
    order = Mock(total=150.0)

    # 3. Выполняем действие
    processor.process(order)

    # 4. Проверяем, что метод мока был вызван с правильными данными
    mock_gateway.charge.assert_called_with(150.0)