Как протоколы помогают при написании модульных тестов в Swift?

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

Ответ

Протоколы являются ключевым инструментом зависимостей (Dependency Injection) и изоляции тестируемого модуля. Они позволяют заменить реальные, сложные или недетерминированные зависимости на контролируемые заглушки (mocks/stubs).

Конкретные преимущества:

  1. Абстракция зависимостей: Тестируемый класс зависит не от конкретной реализации, а от протокола.
  2. Создание моков: Легко создать «подставную» реализацию протокола для тестов.
  3. Проверка взаимодействий: Мок может фиксировать факт и параметры вызовов своих методов.

Пример архитектуры с протоколом:

// 1. Протокол зависимости
protocol DataFetcherProtocol {
    func fetchItems(completion: @escaping (Result<[Item], Error>) -> Void)
}

// 2. Реальная реализация (не используется в тестах)
class NetworkDataFetcher: DataFetcherProtocol { ... }

// 3. Мок-реализация для тестов
class MockDataFetcher: DataFetcherProtocol {
    var fetchItemsWasCalled = false
    var forcedResult: Result<[Item], Error>?

    func fetchItems(completion: @escaping (Result<[Item], Error>) -> Void) {
        fetchItemsWasCalled = true
        if let result = forcedResult {
            completion(result)
        }
    }
}

// 4. Тестируемый ViewModel
class ItemsViewModel {
    private let fetcher: DataFetcherProtocol // Зависимость через протокол
    init(fetcher: DataFetcherProtocol) { self.fetcher = fetcher }
    func loadData() { fetcher.fetchItems { ... } }
}

// 5. Сам тест
func testLoadDataCallsFetcher() {
    let mockFetcher = MockDataFetcher()
    mockFetcher.forcedResult = .success([])
    let viewModel = ItemsViewModel(fetcher: mockFetcher)

    viewModel.loadData()

    XCTAssertTrue(mockFetcher.fetchItemsWasCalled)
}

Такой подход делает тесты быстрыми (нет реальных сетевых запросов), стабильными (нет зависимости от внешних сервисов) и сфокусированными на логике тестируемого модуля.