UI-тестування перевіряє коректність відображення та взаємодії елементів користувацького інтерфейсу мобільного застосунку — кнопок, текстових полів, списків та навігаційних компонентів. На відміну від модульних тестів, що перевіряють бізнес-логіку, UI-тести емулюють дії користувача: дотики, свайпи, введення тексту та перевіряють реакцію інтерфейсу. Згідно з дослідженням Android Developers, 2024, UI-тестування охоплює 70% критичних користувацьких сценаріїв та дозволяє виявити дефекти верстки, недоступні для логічних перевірок.
Головне
UI-тестування — це вид автоматизованої перевірки, при якому тестовий код взаємодіє з графічним інтерфейсом застосунку так само, як це робив би реальний користувач. Тест знаходить на екрані елемент — кнопку, текстове поле, список — виконує над ним дію та перевіряє очікувану реакцію інтерфейсу. Наприклад, після введення невірного пароля UI-тест перевіряє, що на екрані з'явилося повідомлення про помилку з коректним текстом.
Головна відмінність UI-тестів від інших видів автоматизації — вони працюють через Accessibility-шар операційної системи, а не через внутрішні API застосунку. Це означає, що UI-тести бачать інтерфейс точно так само, як користувач та система читання з екрану. Завдяки цьому UI-тести перевіряють не лише функціональність, але й доступність елементів — відповідність вимогам WCAG.
Згідно з опитуванням JetBrains Developer Ecosystem 2023, 58% мобільних команд використовують UI-тести у своєму CI/CD пайплайні. Середнє покриття UI-тестами в комерційних проектах становить 30–40% екранів застосунку. При цьому проекти з UI-тестами на 25% рідше отримують негативні відгуки в магазинах застосунків, пов'язані з падіннями інтерфейсу.
Головна відмінність між UI-тестами та модульними тестами — рівень абстракції. Модульні тести працюють з окремими класами та функціями, ізольованими від фреймворку Android або iOS. Вони виконуються на JVM (для Android) без запуску емулятора та займають мілісекунди. UI-тести запускаються на реальному пристрої або емуляторі, взаємодіють з системними сервісами та потребують секунди або хвилини на один сценарій.
Різниться і цільова аудиторія тестів. UI-тести перевіряють наскрізні користувацькі сценарії — реєстрацію, оформлення замовлення, пошук. Модульні тести покривають бізнес-логіку: розрахунки, валідацію, перетворення даних. UI-тест не перевіряє коректність розрахунку податку — він перевіряє, що підсумкова сума відображається на екрані. Сам розрахунок перевіряється модульним тестом.
За даними Google Testing Blog (2020), оптимальне співвідношення тестів у проекті слідує правилу піраміди тестування: 70% модульних тестів, 20% інтеграційних та 10% UI-тестів. Порушення цієї пропорції на користь UI-тестів призводить до збільшення часу прогону та крихкості тестового набору, оскільки UI-тести чутливі до змін у розмітці екранів.
Для Android домінуючим фреймворком є Espresso — бібліотека від Google, вбудована в AndroidX Test. Espresso автоматично синхронізується з потоком UI, очікуючи завершення анімацій та фонових завдань перед виконанням наступної перевірки. Для Jetpack Compose використовується розширення Compose UI Test, яке працює через семантичні вузли замість традиційних view-ідентифікаторів.
Для iOS основним інструментом є XCUITest, що входить до складу Xcode. Тести пишуться на Swift та використовують Accessibility-ідентифікатори для пошуку елементів. XCUITest підтримує запис тестів через record-функцію та інтеграцію з CI-системами через xcodebuild. Для кроссплатформних проектів застосовується Appium, заснований на WebDriver-протоколі та дозволяє запускати одні й ті самі тести на Android та iOS з мінімальними змінами в коді.
Espresso працює з традиційною View-системою через onView та id-ідентифікатори ресурсів. Compose UI Test використовує семантичний шар, що робить тести менш залежними від ієрархії в'юв. Наприклад, пошук кнопки в Espresso: onView(withId(R.id.submit)), в Compose: onNodeWithTag(“submit”). Compose-тести автоматично обробляють рекомпозицію та не вимагають явних очікувань for idle state.
XCUITest використовує XCUIApplication як точку входу. Кожен елемент інтерфейсу шукається через Accessibility-властивості: accessibilityIdentifier для програмного доступу та accessibilityLabel для VoiceOver. Фреймворк підтримує запис тестів через record-функцію Xcode — розробник виконує дії на симуляторі, а Xcode генерує код тесту. Готові тести запускаються через xcodebuild test.
Appium заснований на WebDriver-протоколі та підтримує будь-які мови: Java, Python, JavaScript. Для пошуку елементів використовуються стратегії id, xpath, class name та accessibility id. Appium вимагає встановлення сервера та налаштування Desired Capabilities — platformName, deviceName, appPackage. Альтернатива — Maestro, що використовує YAML-сценарії та не вимагає компіляції тестового коду.
Розглянемо UI-тести для одного й того самого сценарію — входу в застосунок — на трьох різних фреймворках: Espresso для Android, XCUITest для iOS та Appium для кроссплатформного підходу. Сценарій: ввести логін та пароль, натиснути кнопку входу, перевірити відображення привітального повідомлення.
Тест на Espresso використовує onView для пошуку елемента за ідентифікатором та perform для виконання дії. Метод check з matcher'ом isDisplayed підтверджує, що елемент видимий на екрані.
@RunWith(AndroidJUnit4::class)
class LoginUiTest {
@Rule
@JvmField
val composeTestRule = createComposeRule()
@Test
fun login_withValidCredentials_showsWelcome() {
composeTestRule
.onNodeWithTag("emailField")
.performTextInput("user@example.com")
composeTestRule
.onNodeWithTag("passwordField")
.performTextInput("secret123")
composeTestRule
.onNodeWithTag("loginButton")
.performClick()
composeTestRule
.onNodeWithText("Ласкаво просимо, Користувачу!")
.assertIsDisplayed()
}
}
XCUITest використовує XCUIApplication для доступу до елементів інтерфейсу через Accessibility-ідентифікатори. Методи tap() та exists забезпечують взаємодію та перевірку.
class LoginUITests: XCTestCase {
let app = XCUIApplication()
override func setUp() {
continueAfterFailure = false
app.launch()
}
func testLogin_withValidCredentials_showsWelcome() {
app.textFields["emailField"].tap()
app.textFields["emailField"].typeText("user@example.com")
app.secureTextFields["passwordField"].tap()
app.secureTextFields["passwordField"].typeText("secret123")
app.buttons["loginButton"].tap()
XCTAssertTrue(app.staticTexts["Welcome, User!"].exists)
}
}
Перший принцип — використовуйте Accessibility-ідентифікатори замість текстових міток для пошуку елементів. Текст кнопки може змінитися при локалізації, а ідентифікатор залишиться стабільним. В Android це властивість contentDescription, в iOS — accessibilityIdentifier. Такий підхід робить тести незалежними від мови інтерфейсу та скорочує витрати на підтримку при зміні копірайтингу.
Уникайте sleep() та фіксованих затримок — використовуйте вбудовані механізми очікування фреймворку. Espresso автоматично очікує завершення анімацій та фонових завдань. XCUITest надає XCTAssertTrue з timeout. Явні паузи роблять тести повільнішими та нестабільнішими, особливо на повільних пристроях у CI-оточенні.
Групуйте тести за критичністю: smoke-тести (3–5 ключових сценаріїв) запускаються на кожний коміт, повний набір UI-тестів — перед релізом. За даними Google Testing Blog (2022), UI-тести, що займають більше 30 хвилин у CI, знижують частоту прогонів на 40%, що зменшує їх ефективність як інструменту раннього виявлення регресій.
UI-тести мають ряд обмежень. Чутливість до змін розмітки: зміна ідентифікатора, ієрархії або типу елемента ламає тест навіть при незмінній функціональності. Рішення — використовувати Page Object патерн, що централізує селектори елементів в окремих класах. При зміні розмітки правиться один файл Page Object, а не десятки тестів.
Час виконання: запуск на реальному пристрої або емуляторі займає в 10–50 разів більше часу, ніж модульний тест. Рішення — запускати UI-тести паралельно на декількох пристроях через Firebase Test Lab або AWS Device Farm. Нестабільність (flakiness) — часта проблема CI-прогонів, викликана анімаціями, мережевими затримками або станом емулятора. Для боротьби з flakiness застосовуються автоматичні ретраї впалих тестів та аналітика стабільності кожного тестового сценарію.
Часті запитання
Для середнього екрану достатньо 3–5 UI-тестів: happy path, валідація помилок, порожній стан, зміна орієнтації та Accessibility-перевірка. Складні екрани з безліччю станів — форми замовлення, налаштування — можуть вимагати 10–15 тестів для повного покриття ключових сценаріїв.
Так, Appium та Maestro дозволяють запускати одні й ті самі сценарії на обох платформах. Однак нативні фреймворки — Espresso та XCUITest — забезпечують кращу стабільність, швидкість та доступ до платформних можливостей, які недоступні через WebDriver-проксі.
Для Compose використовується бібліотека Compose UI Test з семантичними матчерами: onNodeWithText, onNodeWithTag, onNodeWithContentDescription. Семантичний шар Compose абстрагує ієрархію в'юв, що робить тести менш крихкими порівняно з традиційним Espresso для View-системи.
Базовий прогін UI-тестів виконується на емуляторах у CI — це швидко та дешево. Фінальну верифікацію перед релізом рекомендується проводити на фізичних пристроях через Firebase Test Lab, щоб врахувати особливості реального заліза: різну роздільну здатність, версії ОС та продуктивність.
Використовуйте паралельний запуск на декількох пристроях, вимикайте анімації на емуляторі через Developer Options, будуйте модульну архітектуру тестів та запускайте smoke-набір на кожний коміт, а повний регресійний прогін — за розкладом або перед релізом.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також