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

Розробка Unit-тестів для iOS-застосунку (XCTest) Уявіть: ViewModel розрослася до 700 рядків, у ній змішана бізнес-логіка, форматування даних і прямі виклики мережі. Додаєте нову фічу — падає старий потік. Відкочуєте — знову працює, але причина незрозуміла. Саме в такій ситуації ми й пишемо unit-т

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, 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 — unit-тести в 6,7 раза швидші.

// 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 днів (у середньому 4 дні) залежно від обсягу кодової бази та поточного рівня архітектурної ізольованості компонентів. Вартість впровадження — від $500 до $1500.

Ми займаємося iOS-розробкою понад 5 років (5+ років досвіду), впровадили unit-тести у 30+ проєктах. Пишіть нам для безкоштовної оцінки — і ваш код стане надійнішим.