Какие проблемы при тестировании создает прямое создание экземпляров зависимостей в коде?

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

Ответ

Да, прямое создание экземпляров (hard-coded dependencies) серьезно затрудняет модульное тестирование.

Основные проблемы:

  1. Невозможность изоляции — нельзя заменить реальную зависимость (API, базу данных, файловую систему) на контролируемый мок или стаб.
  2. Побочные эффекты — тесты могут выполнять реальные сетевые запросы или изменять данные в БД, что делает их медленными, недетерминированными и разрушительными.
  3. Сложность проверки взаимодействий — нельзя проверить, был ли вызван метод зависимости с правильными аргументами.

Пример проблемного кода:

class DataLoader {
    func loadUserData() -> [User] {
        let apiClient = APIClient() // Прямое создание зависимости
        return apiClient.fetchUsers() // Реальный сетевой вызов в тесте
    }
}

Решение — Dependency Injection (DI):

protocol APIClientProtocol {
    func fetchUsers() -> [User]
}

class DataLoader {
    private let apiClient: APIClientProtocol

    // Зависимость внедряется извне
    init(apiClient: APIClientProtocol) {
        self.apiClient = apiClient
    }

    func loadUserData() -> [User] {
        return apiClient.fetchUsers()
    }
}

// В тесте можно использовать мок
class MockAPIClient: APIClientProtocol {
    var fetchUsersCalled = false
    func fetchUsers() -> [User] {
        fetchUsersCalled = true
        return [User.testInstance]
    }
}

Практика: Используйте внедрение зависимостей через инициализатор или свойства, чтобы сделать код тестируемым и соблюсти принцип инверсии зависимостей (Dependency Inversion Principle).