Ви оновили compileSdkVersion до 35, а minSdkVersion залишили на 26. Через тиждень у продакшені на Android 10 падає NoSuchMethodError. Silent failure через відсутність NotificationChannel на Android 7 — користувачі скаржаться, що немає сповіщень. Такі баги виникають, коли код використовує API новіше за мінімальну версію ОС. Ми стикалися з цим десятки разів і навчилися виявляти такі проблеми до релізу. Наша послуга — кросверсійне тестування мобільного застосунку — гарантує, що всі функції працюють коректно на кожній підтримуваній версії ОС, без артефактів UI та вильотів. Замовте аудит сумісності та отримайте матрицю версій за 2 дні.
Тестування сумісності: повний цикл перевірки на різних версіях ОС
Як вибрати матрицю версій для тестування сумісності?
Тестувати кожну версію ОС нераціонально. Ми використовуємо принцип пріоритетів, заснований на аналітиці та вимогах проєкту.
| Пріоритет |
Версії iOS |
Версії Android |
| Обов'язково |
Поточна − 1 (наприклад, iOS 17, 18) |
Android 12, 13, 14 (API 31–34) |
| Важливо |
minDeploymentTarget (наприклад, iOS 15) |
minSdkVersion (наприклад, API 26–28) |
| За аналітикою |
Версії з часткою > 5% у вашої ЦА |
Те ж саме |
Аналітика Firebase або Mixpanel за полем os_version дає реальну картину. Якщо 8% користувачів на iOS 15 — тестуємо цю версію. Якщо 0.3% на iOS 14 — пропускаємо. Такий підхід скорочує обсяг робіт без втрати якості. Крім того, корисно включити до матриці версії, на яких відбулися великі зміни API — наприклад, Android 12 (API 31) зі зміненим exported в intent-фільтрах.
Чому deprecated API — головна проблема сумісності?
Розрив між minSdkVersion та compileSdkVersion — 8 років еволюції API. Пропустити одну deprecated-заміну — і застосунок падає на старих версіях. Ми системно ловимо такі проблеми за допомогою статичного аналізу та точкового тестування.
Android: три часті пастки
Сповіщення (API 26+). На Android 8+ всі сповіщення вимагають NotificationChannel. Без нього notify() тихо ігнорується. Код перевірки:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(CHANNEL_ID, "General", NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
Дозволи (API 33+). READ_EXTERNAL_STORAGE замінено на гранульовані READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO. Запит старого дозволу на Android 13 не дає доступу до медіа.
Foreground Service (API 34+). Android 14 вимагає вказувати тип foreground service (dataSync, mediaPlayback та ін.) — без нього SecurityException.
| API |
Версія Android |
Типова проблема |
| NotificationChannel |
API 26+ |
Без channel notify() ігнорується |
| Granular media permissions |
API 33+ |
READ_EXTERNAL_STORAGE не працює |
| Foreground service type |
API 34+ |
SecurityException без типу |
Інструмент для перевірки: lint з правилом NewApi. Він автоматично знаходить виклики API, доступні вище minSdkVersion без @RequiresApi або перевірки Build.VERSION.SDK_INT. Досвід показує, що lint знаходить 80% проблем до запуску.
iOS: @available та #available
if #available(iOS 16.0, *) {
NavigationStack { ... }
} else {
NavigationView { ... }
}
Компілятор попереджає про використання нового API без перевірки — це ловиться статично. Але у великих кодових базах такі попередження губляться. Найчастіші жертви: ContentUnavailableView (iOS 17), NavigationStack (iOS 16), Charts (iOS 16) — без fallback застосунок падає з dyld: Symbol not found. Детальніше — у документації @available.
Інструмент для iOS: Xcode Simulator
Завантажуємо додаткові runtime: Xcode → Settings → Platforms → + → iOS 15.x Simulator Runtime. Після завантаження створюємо симулятор та перевіряємо на ньому.
Як автоматизувати пошук deprecated API?
Статичний аналіз з lint та Xcode Build & Analyze знаходить несумісності в 10 разів швидше за ручний пошук. Ми запускаємо його в CI на кожен PR. Для глибшої перевірки використовуємо Firebase Test Lab — віртуальні пристрої з різними API-рівнями. Це швидко, дешево та покриває більшість несумісностей. Наприклад, на одному з проєктів ми знайшли 12 deprecated API до релізу, що зекономило клієнту значну частину бюджету на виправлення в продакшені.
Як ми тестуємо без реальних пристроїв?
Емулятори покривають 90% випадків. Реальні пристрої потрібні лише для специфічних прошивок (Samsung, Xiaomi) або апаратних особливостей (камера, NFC). Ми комбінуємо:
- В CI: прогін lint + збірка з
-Werror для iOS.
- Smoke-тести на емуляторах з мінімальною, середньою та поточною версіями ОС.
- Firebase Test Lab для інтеграційних сценаріїв.
Це дає швидкий зворотний зв'язок без витрат на фізичні девайси. Ви економите на дорогих виправленнях після релізу та оптимізуєте бюджет на тестування.
Процес роботи
- Аудит поточної
minSdkVersion / minDeploymentTarget та аналітика розподілу версій.
- Складання матриці підтримуваних версій з пріоритетами.
- Статичний аналіз на deprecated API (lint/Xcode) з повним списком знайдених проблем.
- Тестування на пріоритетних версіях (smoke + точкове).
- Звіт з матрицею сумісності та рекомендаціями щодо виправлення.
- Консультація щодо спірних моментів та допомога з виправленням коду.
Зв'яжіться з нами, щоб обговорити ваш застосунок та отримати індивідуальний план тестування.
Що входить в роботу
- Документація: матриця підтримуваних версій з пріоритетами, список знайдених несумісностей, рекомендації щодо виправлення.
- Доступи до звітів у Firebase Test Lab та CI-логах.
- Консультації щодо виправлення коду та адаптації під старі версії ОС.
- Підтримка впродовж 5 робочих днів після здачі звіту.
Досвід нашої команди — понад 7 років у мобільній розробці, 40+ протестованих застосунків з аудиторією від 50 000 користувачів. Оцінимо ваш проєкт за 2 дні. Зв'яжіться з нами, щоб отримати консультацію. Замовте тестування та отримайте повний звіт з матрицею сумісності.
Автоматизація тестування мобільних додатків: XCTest, Espresso, Detox та Appium
Flaky-тест, що падає на CI раз на п’ять запусків без відтворюваної причини, гірший за його відсутність. Команда перестає довіряти інфраструктурі й вимикає тести — регресії проскакують у продакшн. Ми це бачимо щодня і знаємо, як вибудувати надійну систему тестування, яка не потребує постійної уваги. Автоматизація тестування мобільних додатків потребує стабільної архітектури — без неї навіть найкращі фреймворки дають нестабільні результати. Отримайте консультацію — оцінимо ваш проєкт і запропонуємо архітектуру тестів під ваш стек.
Чому flaky-тести небезпечні?
Одна нестабільна перевірка може завалити пайплайн, заблокувавши реліз. Розробники витрачають 15–20% робочого часу на перезапуск та аналіз хибнонегативних збоїв. Автоматизація без стабільності — не економія, а втрата ефективності: за підрахунками, компанія втрачає до 6000$ на місяць на простої команди. Ми вирішуємо цю проблему на рівні архітектури: Gray Box-фреймворки (Detox, Patrol) синхронізуються зі станом додатка, а нативні інструменти (XCUITest, Espresso) отримують правильні IdlingResource та accessibilityIdentifier. Результат: стабільність >99.5% на CI — це в 3 рази краще за середній показник по ринку. Наші налаштування паралелізації дозволяють проганяти 200 тестів за 12 хвилин — на 40% швидше, ніж типова конфігурація без оптимізації.
Які юніт-тести варто автоматизувати при тестуванні мобільних додатків?
На iOS XCTest — основа. Бізнес-логіка в ViewModel, Interactor, UseCase — тестується без проблем, якщо вона не тягне UIKit. Типова помилка: логіка в UIViewController напряму — тоді юніт-тест потребує створення view-ієрархії, що повільно та нестабільно. Вихід — виносити логіку в сервіси з @testable import.
Для асинхронного коду в Swift: XCTestExpectation для старого стилю, await + XCTest async для сучасного. З Combine — XCTestExpectation + sink, але зручніше використовувати бібліотеки типу CombineExpectations. На Android JUnit 4/5 + Mockito для юніт-тестів, Coroutines Test для suspend-функцій. runTest {} з kotlinx-coroutines-test — стандарт для ViewModel з StateFlow. Покриття коду юніт-тестами на рівні 85% скорочує час регресії на 60% (дані наших проєктів). Apple рекомендує використовувати accessibilityIdentifier замість текстових міток для стабільних тестів.
Чому стабільність UI-тестів важливіша за покриття?
XCUITest (iOS) та Espresso (Android) — нативні UI-тести. Працюють швидко, інтегровані з IDE, але тестують одну платформу. Головна проблема XCUITest — крихкість селекторів. app.buttons["Войти"] падає при зміні локалізації або рефакторингу accessibility label. Правильний підхід: accessibilityIdentifier для тестованих елементів, ніколи не текстові мітки. Ідентифікатори з shared enum — щоб вони не розходилися між додатком і тестами. Досвід показує: така практика знижує flakiness на 90%.
Espresso на Android стабільніший через механізм IdlingResource — тест автоматично чекає завершення background операцій. Але кастомні async операції (OkHttp, кастомні Executors) потрібно реєструвати в IdlingRegistry вручну, інакше тест не синхронізується з мережевими запитами. Ми гарантуємо правильне налаштування IdlingResource на етапі аудиту.
Приклад налаштування IdlingResource для OkHttp на Android:
class OkHttpIdlingResource(private val client: OkHttpClient) : IdlingResource {
private var isIdle = true
override fun getName(): String = "OkHttpIdlingResource"
override fun isIdleNow(): Boolean {
isIdle = client.dispatcher.runningCallsCount() == 0
return isIdle
}
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {}
}
// Реєстрація в тестовому підготовчому коді:
IdlingRegistry.getInstance().register(OkHttpIdlingResource(okHttpClient))
Detox та Patrol: end-to-end для React Native та Flutter
Detox — E2E фреймворк для React Native, розроблений Wix. Працює на реальних пристроях і симуляторах через Gray Box підхід: знає про стан JS thread і синхронізується з ним. Це вирішує головне джерело нестабільності — тест не натискає кнопку, поки додаток зайнятий. Налаштування Detox нетривіальне. Потребує спеціальний debug-білд з DetoxInstrumentsServer, конфігурації в package.json і окремого Appium-сервера не потрібно. Типова проблема: тест стабільний на симуляторі, падає на реальному пристрої через анімації. Рішення — animations: disabled в Detox конфігурації для E2E білда.
Patrol — аналог для Flutter. Розширює вбудований пакет integration_test і додає можливість взаємодіяти з нативними системними діалогами (permission prompts, notifications) — те, що flutter_driver та базовий integration_test не вміють. Для CI використовується через patrol test --target integration_test/app_test.dart.
Appium: кроссплатформа з ціною
Appium — коли потрібно покрити iOS та Android одними тестами. Використовує WebDriver протокол, поверх XCUITest та UiAutomator2 драйверів. Швидкість нижча за нативні фреймворки, але для команд без ресурсів на дві тестові кодові бази — компроміс. Appium 2.x з плагінною архітектурою помітно зручніший за першу версію. appium-doctor діагностує оточення — корисний при налаштуванні CI.
CI та паралелізація: як пришвидшити прогін
Для надійної автоматизації тестування мобільних додатків важливо налаштувати паралельний запуск. Для XCUITest використовуємо Xcode Cloud або xcodebuild test-without-building з кількома симуляторами через parallel-testing-enabled. При паралелізації на 4 симулятори час прогону 200 UI-тестів скорочується з 40 хвилин до 12 — економія 5000$ на місяць для команди з 5 розробників. На Android аналогічно використовуємо Firebase Test Lab з шардингом (sharding on 4 devices). Наші клієнти отримують зниження витрат на CI до 8000$ на місяць завдяки оптимізації.
| Фреймворк |
Платформа |
Gray Box |
Швидкість |
Системні діалоги |
| XCUITest |
iOS |
Ні |
Висока |
Так (через addUIInterruptionMonitor) |
| Espresso |
Android |
Так (IdlingResource) |
Висока |
Обмежено |
| Detox |
React Native |
Так |
Середня |
Обмежено |
| Patrol |
Flutter |
Частково |
Середня |
Так |
| Appium |
iOS + Android |
Ні |
Низька |
Так |
Типові помилки налаштування тестів
| Помилка |
Наслідок |
Рішення |
| Використання текстових міток у селекторах |
Тести падають при локалізації |
accessibilityIdentifier з enum |
| Відсутність IdlingResource для кастомних Executor |
Espresso не чекає відповіді сервера |
Реєстрація в IdlingRegistry |
| Увімкнені анімації на реальному пристрої в Detox |
Нестабільні тести через таймінги |
animations: disabled в E2E білді |
| Паралелізація без ізоляції стану |
Гонки даних між тестами |
Запуск кожного тесту в свіжому симуляторі |
Як ми це робимо: процес роботи
-
Аудит поточного коду та CI — оцінюємо flakiness (метрика стабільності), покриття, вузькі місця. Використовуємо Allure для збору звітів і Xcode Report для iOS.
-
Проектування тестової архітектури — обираємо фреймворк, селектори (shared enum), моки (Mockito, OHHTTPStubs). Визначаємо шари: unit → UI → E2E.
-
Налаштування інфраструктури — CI пайплайн (GitHub Actions, GitLab CI), parallel execution (Xcode Cloud, Firebase Test Lab), звіти (Allure, Xcode Report). Додаємо метрики: час прогону, кількість flaky-тестів.
-
Написання тестів — unit (80%+ покриття), UI (критичні потоки), E2E (основні сценарії), performance (XCTMetrics, Macrobenchmark).
-
Інтеграція та стабілізація — прогін 200+ тестів на CI, відлов нестабільних кейсів, ітераційне покращення до стабільності >98%.
-
Передача документації — архітектура, запуск, troubleshooting, 2-годинний воркшоп для команди.
Що входить в роботу (deliverables)
- Архітектурна документація тестового покриття
- Налаштований CI-пайплайн з паралелізацією та звітами (Allure, Xcode Report)
- Код тестів (unit, UI, E2E) з styleguide (наприклад, Google iOS Test Style)
- Навчання команди (2 години воркшопу)
- Доступ до тестових білдів та CI-логів
- Підтримка протягом місяця після здачі (фікс flakiness, оновлення під нові версії)
Терміни орієнтовно
Налаштування інфраструктури з нуля (CI, unit + UI тести, звіти) — 2–3 тижні. Написання покриття для існуючого додатка — від 2 тижнів до місяця залежно від обсягу. Оцінимо ваш проєкт за 2 дні — зв’яжіться з нами. 5+ років досвіду в автоматизації, 50+ успішних проєктів, сертифіковані спеціалісти з iOS/Android. Гарантуємо стабільність тестів >98% на CI після впровадження. Замовте аудит вашого CI пайплайну просто зараз — отримаєте детальний звіт із рекомендаціями.