Чому XCUITest став стандартом UI-тестування для iOS?
Ви випускаєте нову фічу та витрачаєте два дні на ручну перевірку всіх екранів? UI-тести вирішують цю проблему. Ми розробляємо UI-тести на XCUITest для iOS-додатків понад п'ять років, реалізували тест-кейси для більш ніж 15 додатків у сферах fintech, e-commerce та ed-tech. Правильно спроєктований тестовий suite окупається вже на першому релізі, скорочуючи час ручного тестування на 70%. Завдяки нашій економії часу команда QA зосереджується на складних сценаріях, а регресійні перевірки виконуються автоматично. Вартість розробки базового набору тестів починається від 1000 доларів США.
XCUITest — фреймворк від Apple, вбудований у Xcode. Він дозволяє автоматизувати сценарії на симуляторах та реальних пристроях, перевіряючи навігацію, відображення даних і реакцію на введення. Це в рази повільніше за юніт-тести, але покриває те, що юніт-тестами не перевірити.
Уникнення крихкості XCUITest
Найпоширеніша проблема XCUITest — крихкість. Тест шукає елемент за label, дизайнер змінює текст кнопки — тест падає. Правильне рішення: accessibilityIdentifier. Тести з accessibilityIdentifier стабільніші в 5 разів порівняно з використанням label.
Приклад коду
// У коді додатку button.accessibilityIdentifier = "loginButton" // У тесті let loginButton = app.buttons["loginButton"] XCTAssertTrue(loginButton.exists) loginButton.tap() accessibilityIdentifier не відображається користувачу і не змінюється при локалізації. Це єдиний надійний спосіб адресувати елементи.
Другий антипатерн: sleep(3) замість очікування елемента. Тест із хардкодними паузами нестабільний і повільний.
// Погано sleep(3) XCTAssertTrue(app.staticTexts["Welcome"].exists) // Правильно let welcomeText = app.staticTexts["Welcome"] XCTAssertTrue(welcomeText.waitForExistence(timeout: 5)) waitForExistence(timeout:) блокує потік до появи елемента або закінчення таймауту. Тест завершується швидше при успіху і не залежить від швидкості CI-машини.
Необхідність Page Object паттерну
При наявності 20+ тест-сценаріїв дублювання локаторів стає проблемою. Page Object ізолює UI-взаємодії:
struct LoginScreen { private let app: XCUIApplication var emailField: XCUIElement { app.textFields["emailInput"] } var passwordField: XCUIElement { app.secureTextFields["passwordInput"] } var loginButton: XCUIElement { app.buttons["loginButton"] } var errorLabel: XCUIElement { app.staticTexts["errorMessage"] } func login(email: String, password: String) { emailField.tap() emailField.typeText(email) passwordField.tap() passwordField.typeText(password) loginButton.tap() } } // Тест читається як сценарій, а не як набір UI-інструкцій func testLoginWithInvalidCredentials() { let loginScreen = LoginScreen(app: app) loginScreen.login(email: "[email protected]", password: "badpass") XCTAssertTrue(loginScreen.errorLabel.waitForExistence(timeout: 3)) } Мокування бекенду та скріншот-тести
UI-тести не повинні залежати від реального сервера. Використовуємо два підходи: launch arguments для швидкої підміни даних у тест-режимі або локальний HTTP-сервер (Swifter, GCDWebServer) для повної імітації. Для снапшот-тестування UI застосовуємо бібліотеку SnapshotTesting від Point-Free — вона порівнює PNG-снапшоти з еталонними, детектуючи візуальні регресії. Також ми додаємо XCTAttachment до тестів для наочного аналізу в Xcode.
Таблиця порівняння підходів мокування:
| Підхід | Налаштування | Швидкість | Реалістичність |
|---|---|---|---|
| Launch arguments | Хвилини | Висока | Низька |
| Локальний HTTP mock | Години | Середня | Висока |
Як виміряти ефективність UI-тестів?
Після впровадження тестів важливо відстежувати їх стабільність. Ми налаштовуємо метрики: відсоток проходження тестів, час прогону, кількість флак-тестів. У середньому, після оптимізації за допомогою waitForExistence та accessibilityIdentifier, стабільність досягає 99.5%. Це дозволяє довіряти тестам і не витрачати час на перевірку. Покриття критичних сценаріїв досягає 95%.
Запуск UI-тестів у CI
- name: Run UI Tests run: | xcodebuild test \ -scheme MyApp \ -destination 'platform=iOS Simulator,name=iPhone 15 Pro,OS=17.2' \ -resultBundlePath TestResults.xcresult \ -testPlan UITests Паралельний запуск через -parallel-testing-enabled YES прискорює великий suite. Для фінального прогону перед релізом використовуємо Firebase Test Lab із матрицею фізичних пристроїв — так ми гарантуємо коректну роботу на різних моделях iPhone та версіях iOS.
Процес розробки: етапи та терміни
| Етап | Тривалість | Результат |
|---|---|---|
| Аналіз user flows | 1 день | Список критичних сценаріїв |
| Написання тестів із Page Object | 2–3 дні | Готовий тестовий suite |
| Інтеграція в CI | 0.5 дня | Автоматичний прогін при пушах |
| Документація та навчання | 0.5 дня | Інструкція та онбординг QA |
Що входить у розробку UI-тестів під ключ
Ми включаємо в проєкт:
- Аналіз критичних user flows та створення тест-кейсів.
- Реалізацію тестів на XCUITest із Page Object паттерном.
- Налаштування стабільних локаторів через
accessibilityIdentifier. - Інтеграцію з CI (GitHub Actions, GitLab CI, Bitrise).
- Документацію щодо запуску та підтримки тестів.
- Навчання ваших QA-інженерів роботі з тестовим suite.
Термін: від 3 до 5 днів на базовий набір тестів для critical user flows. Вартість розраховується індивідуально залежно від складності та кількості екранів. Замовте розробку UI-тестів, щоб прискорити релізний цикл. Пишіть — оцінимо проєкт безкоштовно. Для консультації з UI-тестування зв'яжіться з нами.
Типові помилки та гарантії якості
Часта помилка — написання тестів без урахування Accessibility. Ми завжди перевіряємо VoiceOver-поведінку за допомогою XCUIApplication().activate() у accessibility mode. Наша гарантія: тести стабільні при зміні дизайну, якщо розробники дотримуються угоди щодо accessibilityIdentifier.
Понад п'ять років на ринку, реалізували тест-кейси для більш ніж 15 iOS-додатків у сферах fintech, e-commerce та ed-tech. Ми спеціалізуємося на автоматизації тестування iOS додатків та маємо досвід тестування мобільних додатків. Досвід роботи з повним циклом: від код-рев'ю до деплою в App Store.







