TL;DR

  • Appium автоматизирует iOS и Android приложения через WebDriver протокол — один фреймворк, обе платформы
  • Настройка: Appium сервер, SDK платформ (Android Studio/Xcode), клиентская библиотека
  • Поиск элементов по accessibility id, xpath или платформенным локаторам
  • Поддержка жестов (свайп, скролл, тап), реальных устройств и эмуляторов/симуляторов
  • Интеграция с CI/CD через Appium в Docker или облачные сервисы (BrowserStack, Sauce Labs)

Идеально для: QA-команд, тестирующих мобильные приложения на разных платформах Пропусти, если: Тестируешь только Android (используй Espresso) или только iOS (используй XCUITest) Время чтения: 20 минут

Твоё приложение работает на iOS. Падает на Android. Или наоборот. Ручное тестирование на обеих платформах удваивает усилия. Паритет функций становится кошмаром.

Appium решает кросс-платформенное мобильное тестирование. Пишешь один тест, запускаешь на iOS и Android. Один язык, один фреймворк, один CI-пайплайн.

Этот туториал покрывает Appium от настройки до CI/CD интеграции — всё, что нужно для автоматизации мобильных приложений.

Что такое Appium?

Appium — это open-source фреймворк для мобильной автоматизации. Он автоматизирует нативные, гибридные и мобильные веб-приложения на iOS и Android через протокол WebDriver.

Как работает Appium:

  1. Твой тестовый код отправляет команды на Appium сервер
  2. Appium переводит команды в платформенные действия
  3. UIAutomator2 (Android) или XCUITest (iOS) выполняет действия
  4. Результаты возвращаются по той же цепочке

Почему Appium:

  • Кросс-платформенность — одни тесты для iOS и Android
  • Независимость от языка — Java, Python, JavaScript, Ruby, C#
  • Без модификации приложения — тестирует реальные продакшен-билды
  • Стандарт WebDriver — знакомый API для пользователей Selenium
  • Open-source — бесплатный, активное сообщество, регулярные обновления

Настройка окружения

Предварительные требования

Для Android:

  • Java JDK 11+
  • Android Studio с SDK
  • Переменная окружения ANDROID_HOME
  • USB-отладка включена на устройстве

Для iOS (только macOS):

  • Xcode с Command Line Tools
  • iOS Симулятор или реальное устройство
  • Аккаунт Apple Developer (для реальных устройств)

Установка Appium

# Установка Appium 2.x глобально
npm install -g appium

# Проверка установки
appium --version

# Установка драйверов платформ
appium driver install uiautomator2  # Android
appium driver install xcuitest      # iOS

# Список установленных драйверов
appium driver list --installed

Appium Inspector

Установи Appium Inspector для инспекции элементов:

  • Скачай с GitHub releases
  • Или используй веб-версию на inspector.appiumpro.com

Desired Capabilities

Capabilities сообщают Appium, какое устройство и приложение использовать.

Android Capabilities

from appium import webdriver
from appium.options.android import UiAutomator2Options

options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "Pixel 6"
options.app = "/path/to/app.apk"
options.automation_name = "UiAutomator2"

# Опционально, но рекомендуется
options.no_reset = True  # Не сбрасывать состояние приложения
options.full_reset = False
options.new_command_timeout = 300

driver = webdriver.Remote("http://localhost:4723", options=options)

iOS Capabilities

from appium import webdriver
from appium.options.ios import XCUITestOptions

options = XCUITestOptions()
options.platform_name = "iOS"
options.device_name = "iPhone 14"
options.platform_version = "16.0"
options.app = "/path/to/app.app"
options.automation_name = "XCUITest"

# Для реальных устройств
options.udid = "device-udid-here"

driver = webdriver.Remote("http://localhost:4723", options=options)

Тестирование установленных приложений

# Android - используй package и activity
options.app_package = "com.example.myapp"
options.app_activity = "com.example.myapp.MainActivity"

# iOS - используй bundle ID
options.bundle_id = "com.example.myapp"

Поиск элементов

Стратегии локаторов

from appium.webdriver.common.appiumby import AppiumBy

# Accessibility ID (рекомендуется - работает кросс-платформенно)
element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")

# ID (Android resource-id)
element = driver.find_element(AppiumBy.ID, "com.example:id/login_button")

# XPath (медленнее, но гибко)
element = driver.find_element(AppiumBy.XPATH, "//android.widget.Button[@text='Login']")

# Class name
element = driver.find_element(AppiumBy.CLASS_NAME, "android.widget.EditText")

# Android UIAutomator
element = driver.find_element(
    AppiumBy.ANDROID_UIAUTOMATOR,
    'new UiSelector().text("Login")'
)

# iOS predicate string
element = driver.find_element(
    AppiumBy.IOS_PREDICATE,
    'label == "Login" AND type == "XCUIElementTypeButton"'
)

# iOS class chain
element = driver.find_element(
    AppiumBy.IOS_CLASS_CHAIN,
    '**/XCUIElementTypeButton[`label == "Login"`]'
)

Ожидание элементов

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

wait = WebDriverWait(driver, 10)

# Ожидание видимости элемента
element = wait.until(
    EC.visibility_of_element_located((AppiumBy.ACCESSIBILITY_ID, "welcome_message"))
)

# Ожидание кликабельности
element = wait.until(
    EC.element_to_be_clickable((AppiumBy.ACCESSIBILITY_ID, "submit_button"))
)

# Ожидание исчезновения элемента
wait.until(
    EC.invisibility_of_element_located((AppiumBy.ACCESSIBILITY_ID, "loading_spinner"))
)

Базовые взаимодействия

Тап, ввод текста, очистка

# Тап/Клик
login_button = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")
login_button.click()

# Ввод текста
username_field = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "username")
username_field.send_keys("testuser")

# Очистка поля
username_field.clear()

# Получение текста
message = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "message")
print(message.text)

# Проверка отображения/активности
assert login_button.is_displayed()
assert login_button.is_enabled()

Работа с клавиатурой

# Скрыть клавиатуру
driver.hide_keyboard()

# Проверить, показана ли клавиатура (Android)
is_keyboard_shown = driver.is_keyboard_shown()

Жесты

Свайп и скролл

from appium.webdriver.common.touch_action import TouchAction

# Простой свайп (start_x, start_y, end_x, end_y, duration_ms)
driver.swipe(500, 1500, 500, 500, 800)

# Скролл к элементу (Android)
driver.find_element(
    AppiumBy.ANDROID_UIAUTOMATOR,
    'new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text("Target Text"))'
)

# Скролл по направлению
def scroll_down(driver):
    size = driver.get_window_size()
    start_x = size['width'] // 2
    start_y = size['height'] * 0.8
    end_y = size['height'] * 0.2
    driver.swipe(start_x, start_y, start_x, end_y, 500)

def scroll_up(driver):
    size = driver.get_window_size()
    start_x = size['width'] // 2
    start_y = size['height'] * 0.2
    end_y = size['height'] * 0.8
    driver.swipe(start_x, start_y, start_x, end_y, 500)

W3C Actions (рекомендуется)

from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput

# Тап по координатам
actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
actions.w3c_actions.pointer_action.move_to_location(100, 200)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.pause(0.1)
actions.w3c_actions.pointer_action.release()
actions.perform()

# Долгое нажатие
element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "item")
actions = ActionChains(driver)
actions.click_and_hold(element).pause(2).release().perform()

Полный пример теста

import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class TestLoginFlow:
    @pytest.fixture(autouse=True)
    def setup(self):
        options = UiAutomator2Options()
        options.platform_name = "Android"
        options.device_name = "emulator-5554"
        options.app = "./app-debug.apk"
        options.automation_name = "UiAutomator2"
        options.no_reset = False

        self.driver = webdriver.Remote("http://localhost:4723", options=options)
        self.wait = WebDriverWait(self.driver, 15)
        yield
        self.driver.quit()

    def test_successful_login(self):
        # Ввод имени пользователя
        username = self.wait.until(
            EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "username_input"))
        )
        username.send_keys("testuser@example.com")

        # Ввод пароля
        password = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "password_input")
        password.send_keys("securepassword123")

        # Скрыть клавиатуру
        self.driver.hide_keyboard()

        # Нажать кнопку входа
        login_btn = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")
        login_btn.click()

        # Проверить приветствие
        welcome = self.wait.until(
            EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "welcome_message"))
        )
        assert "Welcome" in welcome.text

    def test_invalid_credentials(self):
        username = self.wait.until(
            EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "username_input"))
        )
        username.send_keys("wrong@example.com")

        password = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "password_input")
        password.send_keys("wrongpassword")

        self.driver.hide_keyboard()

        login_btn = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")
        login_btn.click()

        # Проверить сообщение об ошибке
        error = self.wait.until(
            EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "error_message"))
        )
        assert "Invalid credentials" in error.text

Паттерн Page Object

# pages/base_page.py
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 15)

    def find(self, locator):
        return self.wait.until(EC.presence_of_element_located(locator))

    def click(self, locator):
        self.find(locator).click()

    def type_text(self, locator, text):
        element = self.find(locator)
        element.clear()
        element.send_keys(text)


# pages/login_page.py
class LoginPage(BasePage):
    USERNAME = (AppiumBy.ACCESSIBILITY_ID, "username_input")
    PASSWORD = (AppiumBy.ACCESSIBILITY_ID, "password_input")
    LOGIN_BTN = (AppiumBy.ACCESSIBILITY_ID, "login_button")
    ERROR_MSG = (AppiumBy.ACCESSIBILITY_ID, "error_message")

    def login(self, username, password):
        self.type_text(self.USERNAME, username)
        self.type_text(self.PASSWORD, password)
        self.driver.hide_keyboard()
        self.click(self.LOGIN_BTN)

    def get_error_message(self):
        return self.find(self.ERROR_MSG).text


# tests/test_login.py
class TestLogin:
    def test_login_success(self, driver):
        login_page = LoginPage(driver)
        login_page.login("user@example.com", "password123")
        # assertions...

Тестирование на реальных устройствах

Android реальное устройство

# Подключить устройство по USB
adb devices

# Получить имя устройства
adb shell getprop ro.product.model

# Включить USB-отладку в настройках разработчика
options.device_name = "Pixel 6"
options.udid = "emulator-5554"  # или реальный серийный номер

iOS реальное устройство

Требуется provisioning profile и подпись:

options.udid = "00008030-001234567890"
options.xcode_org_id = "TEAM_ID"
options.xcode_signing_id = "iPhone Developer"

Облачные фермы устройств

BrowserStack:

from appium import webdriver

options = {
    "platformName": "Android",
    "appium:deviceName": "Samsung Galaxy S23",
    "appium:platformVersion": "13.0",
    "appium:app": "bs://app-id",
    "bstack:options": {
        "userName": "YOUR_USERNAME",
        "accessKey": "YOUR_ACCESS_KEY",
        "projectName": "My Project",
        "buildName": "Build 1.0"
    }
}

driver = webdriver.Remote(
    "https://hub-cloud.browserstack.com/wd/hub",
    options=options
)

Интеграция с CI/CD

GitHub Actions

name: Mobile Tests

on: [push, pull_request]

jobs:
  android-tests:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          pip install appium-python-client pytest
          npm install -g appium
          appium driver install uiautomator2

      - name: Start Android emulator
        uses: reactivecircus/android-emulator-runner@v2
        with:
          api-level: 33
          script: |
            appium &
            sleep 10
            pytest tests/android/ --junitxml=results.xml

      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: results.xml

Appium с помощью ИИ

ИИ-инструменты могут помочь писать и отлаживать Appium-тесты.

Что ИИ делает хорошо:

  • Генерация page objects из скриншотов приложения
  • Предложение стратегий локаторов
  • Отладка ошибок поиска элементов
  • Конвертация тестов между языками

Что всё ещё требует людей:

  • Понимание бизнес-логики приложения
  • Выбор между нестабильными и стабильными локаторами
  • Оптимизация времени выполнения тестов
  • Обработка платформенных edge cases

FAQ

Что такое Appium?

Appium — это open-source фреймворк для мобильной автоматизации тестирования iOS и Android приложений. Он использует протокол WebDriver, позволяя писать тесты на любом языке с WebDriver-клиентом (Java, Python, JavaScript, Ruby, C#). Appium может тестировать нативные, гибридные и мобильные веб-приложения без модификации — тестируешь реальные продакшен-билды.

Appium бесплатный?

Да, Appium полностью бесплатен и open-source под лицензией Apache 2.0. Нет enterprise-версий, премиум-функций или лимитов использования. Весь фреймворк, включая все драйверы (UiAutomator2, XCUITest), доступен бесплатно.

Appium или Espresso/XCUITest — что лучше?

Appium отлично подходит для кросс-платформенного тестирования — пишешь тесты один раз, запускаешь на iOS и Android. Espresso (Android) и XCUITest (iOS) платформенно-специфичны, но быстрее, стабильнее и имеют глубокую интеграцию с ОС. Используй Appium, когда нужно кросс-платформенное покрытие одним кодом. Используй нативные инструменты, когда нужна максимальная скорость и надёжность для одной платформы.

Может ли Appium тестировать и iOS, и Android?

Да, это главное преимущество Appium. Один и тот же тестовый код может работать на обеих платформах с минимальными изменениями — обычно только разные локаторы для платформенных элементов. Пишешь тесты на предпочитаемом языке, а Appium переводит команды в соответствующую платформенную автоматизацию (UiAutomator2 для Android, XCUITest для iOS).

Официальные ресурсы

Смотрите также