← Назад к вопросам

В чём разница между моками и стабами?

2.0 Middle🔥 203 комментариев
#CI/CD и инструменты разработки#Тестирование и отладка

Комментарии (3)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между моками и стабами в iOS-разработке

В контексте модульного тестирования (unit testing) в iOS, моки (mocks) и стабы (stubs) — это два типа тестовых дублёров (test doubles), которые служат разным целям, хотя часто используются вместе. Их ключевое различие заключается в назначении и способе проверки в тестах.

Основные определения

Стабы (Stubs) — это объекты, которые предоставляют предопределённые ответы на вызовы методов во время теста. Их главная задача — заменить реальные зависимости, чтобы изолировать тестируемый код и контролировать его поведение в различных сценариях (например, успех, ошибка, пустые данные).

Моки (Mocks) — это объекты, которые регистрируют вызовы своих методов и позволяют проверить, как тестируемый код взаимодействовал с ними. Их основная цель — верификация поведения (behavior verification), то есть проверка, что определённые методы были вызваны с правильными параметрами, в нужном порядке и нужное количество раз.

Ключевые различия

АспектСтабы (Stubs)Моки (Mocks)
Основная цельКонтроль состояния (state) — предоставление данныхПроверка взаимодействия (interaction) — верификация вызовов
Проверка в тестахПроверяют результат работы тестируемого кодаПроверяют процесс — как вызывались методы зависимостей
Типичные сценарииВозврат успешных/ошибочных данных, симуляция состоянийПроверка отправки сетевых запросов, вызовов делегатов, логирования
ГибкостьЧасто простые, с жёстко заданными ответамиМогут быть сложными, с отслеживанием истории вызовов

Примеры в Swift с XCTest

Пример стаба

// Протокол зависимости
protocol NetworkService {
    func fetchUser(completion: @escaping (Result<User, Error>) -> Void)
}

// Стаб для успешного ответа
class NetworkServiceStub: NetworkService {
    var predefinedUser: User
    
    init(user: User) {
        self.predefinedUser = user
    }
    
    func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {
        completion(.success(predefinedUser)) // Предопределённый ответ
    }
}

// В тесте
func testUserProfileLoadedSuccessfully() {
    let stubUser = User(name: "Иван", age: 30)
    let stubService = NetworkServiceStub(user: stubUser)
    let viewModel = UserViewModel(service: stubService)
    
    viewModel.loadUser()
    
    // Проверяем РЕЗУЛЬТАТ — состояние viewModel
    XCTAssertEqual(viewModel.userName, "Иван")
    XCTAssertEqual(viewModel.userAge, 30)
}

Пример мока

// Мок для проверки взаимодействий
class NetworkServiceMock: NetworkService {
    var fetchUserCallCount = 0
    var lastCompletion: ((Result<User, Error>) -> Void)?
    
    func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {
        fetchUserCallCount += 1 // Регистрируем вызов
        lastCompletion = completion // Сохраняем для последующего управления
    }
}

// В тесте
func testFetchUserCalledExactlyOnce() {
    let mockService = NetworkServiceMock()
    let viewModel = UserViewModel(service: mockService)
    
    viewModel.loadUser()
    
    // Проверяем ВЗАИМОДЕЙСТВИЕ — как вызывался метод
    XCTAssertEqual(mockService.fetchUserCallCount, 1) // Был вызван ровно один раз
}

Практическое применение в iOS

  1. Стабы чаще используют для:

    • Тестирования обработки различных ответов сервера (успех, ошибка 404, 500)
    • Симуляции состояний системы (нет сети, включён/выключен GPS)
    • Замены сложных или медленных зависимостей (база данных, файловая система)
  2. Моки чаще используют для:

    • Проверки отправки аналитических событий или логов
    • Верификации вызовов делегатов или колбэков
    • Тестирования, что определённые методы были вызваны в правильной последовательности
    • Проверки взаимодействия с системными фреймворками (UserNotifications, CoreLocation)

Современные подходы

В современных iOS-проектах часто используют комбинированные подходы:

  • Умные моки (smart mocks), которые могут и возвращать предопределённые значения (как стабы), и верифицировать вызовы
  • Фреймворки для мокинга вроде Cuckoo или Mockingbird, которые генерируют моки автоматически
  • Протокол-ориентированный дизайн, который облегчает создание и тестовых дублёров через протоколы

Заключение

Основное философское различие: стабы помогают тестировать "что" (какой результат получился), а моки — "как" (каким образом этот результат был достигнут). В практике iOS-разработки оба подхода дополняют друг друга: стабы изолируют код от внешних зависимостей, а моки проверяют корректность взаимодействия с этими зависимостями. Эффективное использование обоих типов тестовых дублёров приводит к созданию более надёжных, поддерживаемых и тестируемых приложений.