Розробка E2E-тестів для мобільних додатків (Appium)
Appium — єдиний інструмент у мобільному тестуванні, який охоплює iOS та Android одним тест-кодом. Це його головна перевага та одночасно джерело більшості проблем. За універсальністю стоїть шар абстракції, який іноді поводиться непередбачувано. Написати стійкий Appium-тест складніше, ніж здається.
Архітектура: Appium 2 vs Appium 1
Appium 2 — це не просто версія, це інша концепція. Замість монолітного сервера — ядро плюс окремо встановлювані драйвери:
npm install -g appium@next
appium driver install uiautomator2 # Android
appium driver install xcuitest # iOS
UIAutomator2Driver для Android, XCUITestDriver для iOS. Обидва підтримують W3C WebDriver Protocol, що робить їх сумісними з WebdriverIO, Selenium Grid та стандартними клієнтськими бібліотеками.
Версії, з якими працюємо у 2024–2025:
- Appium 2.5+
-
appium-uiautomator2-driver3.x -
appium-xcuitest-driver7.x -
WebdriverIO8.x (JS/TS) абоAppium-Python-Client4.x (Python)
Налаштування сервера та Capabilities
Найболючіше місце — правильний набір Capabilities. Неправильний platformVersion або відсутній automationName — і сесія не піднімається з непідходящою помилкою.
Мінімальна робоча конфігурація для Android (WebdriverIO):
const capabilities = {
platformName: 'Android',
'appium:automationName': 'UiAutomator2',
'appium:deviceName': 'emulator-5554',
'appium:app': path.resolve('./apps/myapp.apk'),
'appium:newCommandTimeout': 240,
'appium:noReset': false,
};
Для iOS додайте udid пристрою, xcodeOrgId та xcodeSigningId для реального пристрою. На симуляторі простіше — але симулятор не дає результатів по продуктивності та Push Notifications.
Page Object та структура тестів
Сирий Appium-код без паттерну — кошмар для підтримки. Кожен $('//XCUIElementTypeButton[@name="Login"]') дублюється в десятках тестів, і при зміні UI потрібно виправляти все одразу.
Page Object з WebdriverIO:
class LoginPage {
get emailField() { return $('~email_input'); } // accessibilityId
get passwordField() { return $('~password_input'); }
get submitButton() { return $('~login_button'); }
async login(email: string, password: string) {
await this.emailField.setValue(email);
await this.passwordField.setValue(password);
await this.submitButton.click();
}
}
export default new LoginPage();
~ перед строкою — це accessibilityId-локатор. Працює як на iOS (accessibilityIdentifier), так і на Android (contentDescription). Надавайте їй перевагу над XPath — стійкіше при змінах у View-ієрархії.
XPath використовуйте тільки коли немає інших варіантів: //android.widget.TextView[contains(@text,'Додати')]. Але довгі XPath-ланцюги — перша причина нестійких тестів.
Очікування замість sleep
driver.pause(3000) — антипаттерн. Замініть на явні очікування:
await $('~submit_btn').waitForDisplayed({ timeout: 10000 });
await $('~success_screen').waitForExist({ timeout: 15000 });
waitForDisplayed чекає появи елемента у видимій області. waitForExist — просто існування у DOM. Для елементів, які з'являються після анімації — waitForDisplayed з { timeout: 5000, interval: 500 }.
Інтеграція з CI
Appium у CI вимагає запущеного емулятора або підключеного пристрою. Типова схема для GitHub Actions:
- name: Start Android Emulator
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
target: google_apis
arch: x86_64
script: |
appium &
sleep 5
npx wdio run wdio.conf.ts
Для iOS у CI потрібен macOS-раннер. Використовуйте actions/runner на macOS з попередньо встановленим Xcode. Симулятор створіть через xcrun simctl create.
Альтернатива — облачні ферми пристроїв: Firebase Test Lab, BrowserStack App Automate, Sauce Labs. Там емулятор/пристрій та Appium-сервер надаються платформою.
Типові проблеми
StaleElementReferenceError — знайшли елемент, але UI перестроївся до кліку. Обертайте в retry-логіку: await browser.waitUntil(async () => { ... }).
Keyboard covers element — на iOS екранна клавіатура перекриває поле. Перед вводом тексту — await driver.hideKeyboard() або скрол до елемента: await element.scrollIntoView().
App не відповідає на команди після background — 'appium:forceAppLaunch': true у capabilities або явний driver.activateApp(bundleId).
Різні локатори на iOS та Android — навіть з accessibilityId іноді потрібні платформо-специфічні локатори. Вирішуємо через умову:
const selector = driver.isIOS ? '~ios_id' : '~android_id';
Що включено
- Налаштування Appium 2 сервера та драйверів для iOS та Android
- Написання тестів (WebdriverIO/TypeScript або Python)
- Page Object паттерн для всіх ключових екранів
- Інтеграція у CI (GitHub Actions / GitLab CI)
- Налаштування запуску на облачній фермі за запитом
- Звіти Allure або HTML зі скриншотами за кожним кроком
Строки
5 днів — базова конфігурація + охоплення 3–5 ключових флоу. Повне E2E-охоплення великого додатку (15–20 сценаріїв) — 2–3 тижні. Вартість розраховується індивідуально після аналізу додатку та інфраструктури.







