Ответ
Инверсия зависимостей (DIP) — это принцип SOLID, который гласит, что модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. На практике это означает программирование на интерфейсах, а не на конкретных классах. Это напрямую ведет к тестируемости кода.
Проблема без DIP (жёсткая связь):
public class OrderProcessor
{
private readonly SmtpEmailService _emailService; // Конкретный класс
public OrderProcessor()
{
_emailService = new SmtpEmailService(); // Невозможно подменить в тесте
}
public void Process(Order order)
{
// ... логика обработки
_emailService.Send(order.CustomerEmail, "Your order is confirmed");
}
}
// Тестирование такого класса приводит к реальной отправке писем!
Решение с DIP (зависимость от абстракции):
public interface IEmailService
{
Task SendEmailAsync(string to, string subject, string body);
}
public class OrderProcessor
{
private readonly IEmailService _emailService; // Абстракция
// Зависимость внедряется извне (Dependency Injection)
public OrderProcessor(IEmailService emailService)
{
_emailService = emailService;
}
public async Task ProcessAsync(Order order)
{
// ... логика обработки
await _emailService.SendEmailAsync(order.CustomerEmail, "Order Confirmed", "...");
}
}
Как это помогает тестированию:
- Изоляция: В юнит-тесте вы можете подменить реальный
SmtpEmailServiceна мок или стаб, который не отправляет письма, а лишь фиксирует факт вызова. - Контроль над зависимостями: Вы можете легко симулировать различные сценарии (например, исключение при отправке) для проверки отказоустойчивости основного кода.
Пример теста с мок-объектом (используя Moq):
[Test]
public void ProcessOrder_Should_SendConfirmationEmail()
{
// Arrange
var mockEmailService = new Mock<IEmailService>();
var processor = new OrderProcessor(mockEmailService.Object);
var testOrder = new Order { CustomerEmail = "test@example.com" };
// Act
processor.ProcessAsync(testOrder).Wait();
// Assert
mockEmailService.Verify(
s => s.SendEmailAsync(
"test@example.com",
"Order Confirmed",
It.IsAny<string>()),
Times.Once); // Проверяем, что метод был вызван ровно один раз с ожидаемыми параметрами
}
Таким образом, DIP через внедрение зависимостей делает код слабо связанным и легко тестируемым, позволяя проверять логику модуля в полной изоляции от его внешних зависимостей.