Имитация на основе протоколов и внедрение зависимостей
Внедряйте тестовые замены через протоколы без сторонних фреймворков имитации.
«Имитация на основе протоколов и внедрение зависимостей» — бесплатный урок Swift Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Зачем нужны макеты на основе протоколов
Использование протоколов в качестве точек подмены позволяет заменять реальные реализации тестовыми заменами без изменения рабочего кода.
protocol NetworkService {
func fetch(url: URL) async throws -> Data
}
class RealNetworkService: NetworkService { /* URLSession */ }
class MockNetworkService: NetworkService { /* returns fixtures */ }Определение протокола
Выделите протокол из зависимости, включив в него только методы, необходимые вашему классу.
protocol UserRepository {
func getUser(id: Int) async throws -> User
func saveUser(_ user: User) async throws
}Внедрение через инициализатор
Передавайте зависимость через инициализатор, чтобы тесты могли предоставить макет.
class ProfileViewModel {
private let repo: UserRepository
init(repo: UserRepository = RealUserRepository()) {
self.repo = repo
}
}Создание макета
Реализуйте протокол с управляемым состоянием: записывайте вызовы и возвращайте значения-заглушки.
class MockUserRepo: UserRepository {
var fetchedIds: [Int] = []
var stubbedUser: User = User(id: 1, name: "Test")
func getUser(id: Int) async throws -> User {
fetchedIds.append(id)
return stubbedUser
}
func saveUser(_ user: User) async throws {}
}Возвращаемые значения заглушки
Настраивайте заглушки перед тестом, чтобы управлять возвращаемым макетом результатом и моделировать разные сценарии.
let mock = MockUserRepo()
mock.stubbedUser = User(id: 42, name: "Alice")
let vm = ProfileViewModel(repo: mock)
await vm.loadProfile(id: 42)
XCTAssertEqual(vm.userName, "Alice")Проверка взаимодействий
После выполнения действий над тестируемой системой убедитесь, что макет был вызван с ожидаемыми аргументами.
XCTAssertEqual(mock.fetchedIds, [42], "Should fetch user 42 exactly once")Моделирование ошибок
Вызывайте ошибки из макета, чтобы проверить пути обработки ошибок в рабочем коде.
class FailingRepo: UserRepository {
func getUser(id: Int) async throws -> User { throw NetworkError.notFound }
func saveUser(_ user: User) async throws { throw NetworkError.serverError(500) }
}Внедрение через свойство
Другой вариант — предоставить зависимость в виде свойства с возможностью изменения для более простой замены в тестах.
class OrderService {
var paymentGateway: PaymentGateway = StripeGateway()
}
let service = OrderService()
service.paymentGateway = MockPaymentGateway()Внедрение через @Environment / @EnvironmentObject
Приложения SwiftUI внедряют зависимости через окружение, благодаря чему представления можно тестировать, передавая объекты окружения с макетами.
let mockStore = MockCartStore()
let view = CartView().environmentObject(mockStore)Как не использовать слишком много макетов
Создавайте макеты только для прямых зависимостей; используйте реальные взаимодействующие компоненты, если они детерминированы и работают быстро.
// OK to use real DateFormatter in tests — it's deterministic
// Mock URLSession — network I/O is slow and non-deterministicКомпозиция протоколов для более узких интерфейсов
Используйте небольшие специализированные протоколы (принцип разделения интерфейсов), чтобы макетам приходилось реализовывать только необходимое.
protocol Readable { func read() async throws -> [Item] }
protocol Writable { func write(_ item: Item) async throws }
typealias ReadWrite = Readable & WritableБыстрая проверка
Каково основное преимущество внедрения зависимостей через протокол, а не через конкретный тип?
Итоги урока
Определяйте точку подмены на основе протокола для каждой зависимости. Внедряйте зависимости через инициализатор. Реализуйте макеты, которые записывают вызовы и возвращают значения-заглушки. Моделируйте ошибки с помощью макетов, способных выдавать ошибки. Проверяйте взаимодействия с помощью XCTAssertEqual для записанных массивов вызовов.
Часто задаваемые вопросы
Урок «Имитация на основе протоколов и внедрение зависимостей» бесплатный?
Да — полный текст урока «Имитация на основе протоколов и внедрение зависимостей» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Имитация на основе протоколов и внедрение зависимостей»?
Внедряйте тестовые замены через протоколы без сторонних фреймворков имитации. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Имитация на основе протоколов и внедрение зависимостей»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Настройка XCTestCase, очистка и методы тестирования
- Семейство XCTAssert и утверждения с выбрасыванием ошибок
- Имитация на основе протоколов и внедрение зависимостей
- Асинхронные тесты и измерение производительности