Ответ
Да, прямое создание экземпляров (hard-coded dependencies) серьезно затрудняет модульное тестирование.
Основные проблемы:
- Невозможность изоляции — нельзя заменить реальную зависимость (API, базу данных, файловую систему) на контролируемый мок или стаб.
- Побочные эффекты — тесты могут выполнять реальные сетевые запросы или изменять данные в БД, что делает их медленными, недетерминированными и разрушительными.
- Сложность проверки взаимодействий — нельзя проверить, был ли вызван метод зависимости с правильными аргументами.
Пример проблемного кода:
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).