Каким принципам следовать для написания кода, хорошо покрываемого тестами?

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

Ответ

Для написания тестируемого кода следуйте этим принципам:

1. Dependency Injection (DI) — передавайте зависимости извне, а не создавайте внутри класса.

Плохой пример (нетестируемый):

class DataManager {
    func process() {
        let data = DatabaseService().fetch() // Жёсткая зависимость
        // ...
    }
}

Хороший пример (тестируемый):

protocol DatabaseServiceProtocol {
    func fetch() -> Data
}

class DataManager {
    private let service: DatabaseServiceProtocol

    init(service: DatabaseServiceProtocol) {
        self.service = service
    }

    func process() -> Data {
        return service.fetch()
    }
}

2. Разделение ответственности (Single Responsibility) — каждый класс/функция должен делать одну вещь.

3. Программирование на интерфейсах, а не на реализациях — используйте протоколы для абстракции.

4. Избегайте глобального состояния и синглтонов — они усложняют изоляцию тестов.

5. Пишите чистые функции — одинаковые входные данные всегда дают одинаковый результат.

// Чистая функция (легко тестировать)
func calculateTotal(price: Double, quantity: Int) -> Double {
    return price * Double(quantity)
}

6. Используйте моки и стабы для замены реальных зависимостей в тестах.

Ключевой результат: Код, который можно протестировать изолированно, без запуска внешних сервисов, баз данных или сетевых запросов.