0Pricing
Swift Academy · Урок

Имитация на основе протоколов и внедрение зависимостей

Внедряйте тестовые замены через протоколы без сторонних фреймворков имитации.

«Имитация на основе протоколов и внедрение зависимостей» — бесплатный урок 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 — локальная установка не требуется.

Все уроки этого курса

  1. Настройка XCTestCase, очистка и методы тестирования
  2. Семейство XCTAssert и утверждения с выбрасыванием ошибок
  3. Имитация на основе протоколов и внедрение зависимостей
  4. Асинхронные тесты и измерение производительности
← Назад к Swift Academy