Разработка Unit-тестов для iOS-приложения (XCTest)

Разработка Unit-тестов для iOS-приложения (XCTest) Представьте: ViewModel разрослась до 700 строк, в ней смешана бизнес-логика, форматирование данных и прямые вызовы сети. Добавляете новую фичу — падает старый поток. Откатываете — снова работает, но причина неясна. Именно в такой ситуации мы и пи

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Разработка Unit-тестов для iOS-приложения (XCTest)
Средний
~3-5 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    597

Разработка Unit-тестов для iOS-приложения (XCTest)

Представьте: ViewModel разрослась до 700 строк, в ней смешана бизнес-логика, форматирование данных и прямые вызовы сети. Добавляете новую фичу — падает старый поток. Откатываете — снова работает, но причина неясна. Именно в такой ситуации мы и пишем unit-тесты на XCTest. Наш подход — не гнаться за 100% покрытием, а обеспечить возможность рефакторить без страха. Мы тестируем только то, что может сломаться: бизнес-логику, граничные случаи, асинхронные операции. За годы работы над iOS-проектами мы выполнили более 30 внедрений unit-тестов для клиентов из разных сфер — от финтеха до e-commerce. Мы гарантируем стабильность тестов в CI и прозрачный отчёт по покрытию. В этой статье расскажем, как именно мы это делаем и почему XCTest — лучший выбор для iOS.

Какие unit-тесты нужно писать в первую очередь?

Бизнес-логика — главный приоритет. ViewModel, Interactor, UseCase — всё, где есть ветвления if/switch, вычисления, трансформации данных. Протокол-ориентированный подход Swift делает это удобным: зависимости инжектируются через протоколы, в тестах подменяются mock-объектами.

// Протокол сервиса protocol UserServiceProtocol { func fetchUser(id: String) async throws -> User } // Mock для тестов class MockUserService: UserServiceProtocol { var stubbedUser: User? var stubbedError: Error? func fetchUser(id: String) async throws -> User { if let error = stubbedError { throw error } return stubbedUser! } } // Тест func testFetchUserSuccess() async throws { let mockService = MockUserService() mockService.stubbedUser = User(id: "1", name: "Test") let sut = UserViewModel(service: mockService) await sut.loadUser(id: "1") XCTAssertEqual(sut.user?.name, "Test") XCTAssertFalse(sut.isLoading) } 

Как XCTest помогает тестировать асинхронный код?

Современный Swift с async/await тестируется нативно: XCTest поддерживает async тест-функции с iOS 15+ и Xcode 13+. Для Combine-based кода — XCTestExpectation + sink. Мы используем оба подхода в зависимости от архитектуры. В одном из проектов мы заменили 40% медленных интеграционных тестов на unit-тесты с моками, сократив время прогона с 20 минут до 3.

// Combine: тестируем Publisher func testPublisherEmitsValue() { let expectation = expectation(description: "Value received") var cancellables = Set<AnyCancellable>() sut.statePublisher .dropFirst() // пропускаем начальное состояние .sink { state in XCTAssertEqual(state, .loaded) expectation.fulfill() } .store(in: &cancellables) sut.loadData() waitForExpectations(timeout: 2) } 

Граничные случаи — то, что реально падает в продакшене: пустой массив, nil-значение, строка с юникодом, дата в другом timezone. Не только happy path, а именно edge cases.

Архитектура, удобная для тестирования

XCTest-тесты пишутся просто, когда архитектура предполагает инверсию зависимостей. MVVM с DI через инициализатор, Clean Architecture с UseCase — тестируются прямо. Singleton-ы и статические методы — нет. Если проект не использует DI, часть работы — рефакторинг перед написанием тестов.

Частая проблема: ViewModel обращается к UserDefaults напрямую, к Date() напрямую. Оба нужно обернуть в протоколы и инжектировать — иначе тесты будут зависеть от системного состояния и времени запуска.

Например, в проекте по доставке еды мы переписали ViewModel на MVVM с DI, после чего покрытие тестами достигло 85%, а время регрессии сократилось вдвое.

Типичные ошибки в iOS unit-тестах

  • @testable import без флага -enable-testing в build settings — импорт не работает в CI
  • Тесты, зависящие от порядка — XCTest не гарантирует порядок выполнения, каждый тест должен быть изолирован через setUp()/tearDown()
  • Реальные сетевые запросы в тестах — тест становится нестабильным и медленным. Всегда мокируем через URLProtocol subclass или URLSession с кастомным URLSessionConfiguration

Сравнение типов тестов

Тип теста Что проверяет Скорость Стабильность
Unit (XCTest) Бизнес-логика, методы Секунды 100%
Интеграционные Взаимодействие модулей Минуты Зависит от окружения
UI (XCUITest) Пользовательские сценарии Минуты 80-90%

Unit-тесты на XCTest дают лучшее соотношение скорость/надёжность. Наши проекты достигают 80% покрытия бизнес-логики, что сокращает время регрессионного тестирования на 40%. Unit-тесты на XCTest в 5 раз быстрее интеграционных тестов для проверки бизнес-логики.

Таблица типичных проблем и решений

Проблема Последствие Решение
Нет инверсии зависимостей Невозможно подменить сервис Инжекция через протоколы
Использование синглтонов Тесты не изолированы Замена на DI-провайдер
Тесты зависят от времени Плавающие результаты Инжекция Date() через протокол

Что входит в работу

  1. Аудит существующего кода и архитектуры
  2. Выделение компонентов для тестирования (минимальный рефакторинг для DI)
  3. Написание unit-тестов на XCTest с моками и стабами
  4. Интеграция в CI (GitHub Actions, Bitrise, GitLab CI)
  5. Отчёт по покрытию (Xcode Coverage Report, xcov)
  6. Рекомендации по поддержке тестов

Процесс работы

Аудит существующего кода → выделение тестируемых компонентов → при необходимости минимальный рефакторинг для DI → написание тестов → интеграция в CI. Отчёт по покрытию после завершения.

Срок: 3–5 дней в зависимости от объёма кодовой базы и текущего уровня архитектурной изолированности компонентов.

Мы занимаемся iOS-разработкой более 5 лет, внедрили unit-тесты в 30+ проектах. Закажите внедрение unit-тестов — и ваш код станет надёжнее.