Как 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)

Ответ 18+ 🔞

Ах ты ж, ёпта, Dependency Injection! Ну это же просто пиздец как удобно для тестов, понимаешь? Всё дело в том, что твой код перестаёт сам себе создавать зависимости, как последний распиздяй, а получает их красиво, сбоку, как нормальный человек.

Соль в чём? Компонент не городит свои зависимости внутри себя, а принимает их, как милостыню, извне. Все эти жёсткие связи между классами — на хуй! Разрываются в момент.

Что это даёт для тестирования, спросишь ты? А вот что, хитрая жопа:

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

Смотри сюда, пример

Код БЕЗ 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)
    # Если нет — тест упадёт, и мы сразу поймём, где накосячили. Волшебство, блядь!

Вот и вся магия, ёпта. Вместо того чтобы бороться с реальными системами в тестах, ты просто подсовываешь им муляж и спокойно проверяешь логику. Чисто, быстро, без нервов. Пиздатый подход, однозначно.