Ответ
Чтобы код был легко тестируемым, нужно проектировать его с учётом слабой связанности и явных зависимостей. Вот ключевые принципы и примеры:
1. Принцип единой ответственности (Single Responsibility): Класс должен решать одну задачу. Это упрощает написание тестов, так как нужно проверять меньше сценариев.
2. Внедрение зависимостей (Dependency Injection): Зависимости должны передаваться извне (через конструктор или свойства), а не создаваться внутри класса. Это позволяет подменять реальные реализации тестовыми заглушками (моками).
Пример плохого кода:
public class OrderProcessor
{
private readonly EmailService _emailService;
public OrderProcessor()
{
// Зависимость жёстко зашита внутри класса
_emailService = new EmailService();
}
public void Process(Order order) { /* ... */ }
}
Пример хорошего, тестируемого кода:
public interface INotificationService
{
void SendConfirmation(Order order);
}
public class OrderProcessor
{
private readonly INotificationService _notificationService;
// Зависимость инжектируется через конструктор
public OrderProcessor(INotificationService notificationService)
{
_notificationService = notificationService;
}
public void Process(Order order)
{
// Логика обработки заказа...
_notificationService.SendConfirmation(order);
}
}
Теперь в юнит-тесте можно легко подменить INotificationService моком и проверить, был ли вызван метод SendConfirmation.
3. Предпочитайте чистые функции: Функции, которые для одних и тех же входных данных всегда возвращают одинаковый результат и не имеют побочных эффектов (не изменяют глобальное состояние), тестировать проще всего.
4. Избегайте статических методов и синглтонов: Они создают скрытые зависимости, которые сложно подменить в изоляции, что приводит к хрупким тестам, зависящим от глобального состояния.