Как писать код, чтобы его было легко тестировать?

«Как писать код, чтобы его было легко тестировать?» — вопрос из категории Тестирование, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

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. Избегайте статических методов и синглтонов: Они создают скрытые зависимости, которые сложно подменить в изоляции, что приводит к хрупким тестам, зависящим от глобального состояния.