Python для автоматизации тестирования мобильных приложений

Курс посвящён использованию Python для построения и поддержки автотестов мобильных приложений на Android и iOS. Вы изучите инструменты (Appium, pytest), принципы проектирования тестов, интеграцию в CI/CD и практики стабильного запуска тестов на устройствах и в облаках.

1. Основы Python и инструменты тестировщика

Основы Python и инструменты тестировщика

Зачем Python в мобильной автоматизации

Python часто выбирают для автоматизации тестирования мобильных приложений из-за простого синтаксиса, богатой экосистемы библиотек и удобных инструментов для тестирования.

В рамках курса мы будем собирать рабочий стек для автотестов:

  • Python как язык тестов и вспомогательных утилит
  • Pytest как тестовый раннер
  • Appium как драйвер для управления приложением на Android и iOS
  • ADB как инструмент работы с Android-устройством
  • Линтеры и форматтеры, чтобы код был стабильным и поддерживаемым
  • !Общая картина того, как тесты на Python управляют мобильным приложением через Appium и вспомогательные инструменты

    Установка и проверка окружения

    Установка Python

    Рекомендуемая версия для курса: Python 3.11 или 3.12.

  • Скачайте Python с официального сайта: Python
  • Проверьте установку командой python --version или python3 --version
  • Если в системе несколько Python, на Windows часто используется py -V, а на macOS и Linux — python3 --version.

    Редактор кода

    Вам нужен редактор, который удобен для тестов, виртуальных окружений и запуска pytest.

  • PyCharm
  • Visual Studio Code
  • Виртуальные окружения и зависимости

    В автотестах важно, чтобы зависимости проекта не смешивались с другими проектами на вашей машине. Для этого используют виртуальные окружения.

    Создание и активация venv

    venv — встроенный в Python инструмент для изоляции зависимостей.

    Таблица типовых команд:

    | ОС | Создать окружение | Активировать | Деактивировать | |---|---|---|---| | Windows (PowerShell) | python -m venv .venv | .venv\\Scripts\\Activate.ps1 | deactivate | | macOS/Linux | python3 -m venv .venv | source .venv/bin/activate | deactivate |

    Обычно папку .venv добавляют в .gitignore, чтобы не хранить её в репозитории.

    Установка пакетов через pip

    pip — менеджер пакетов Python.

  • Установить пакет: pip install pytest
  • Сохранить зависимости: pip freeze > requirements.txt
  • Установить зависимости из файла: pip install -r requirements.txt
  • Документация:

  • pip
  • venv
  • Быстрый старт Python: базовые конструкции

    Переменные и типы

    В Python тип у значения, а не у переменной.

    Полезная практика для тестов: явно приводить типы там, где данные приходят из внешних источников (API, файлы, capability, переменные окружения).

    Коллекции: list, dict, set, tuple

    Коллекции используются постоянно: для тестовых данных, конфигураций, параметризации.

    Частые операции:

  • Взять элемент списка: devices[0]
  • Взять значение из словаря: caps["platformName"]
  • Безопасно взять значение: caps.get("platformVersion")
  • Условия и циклы

    Циклы часто применяются для перебора тестовых данных.

    Функции: основа переиспользования в тестах

    Функции нужны, чтобы не копировать код: логин, навигация, ожидания, подготовка данных.

    Аргументы по умолчанию полезны для конфигураций:

    Важно: не используйте изменяемые значения как аргументы по умолчанию.

    Неправильно:

    Правильно:

    Модули, импорты и структура проекта

    Когда тестов становится больше, код нужно раскладывать по модулям.

    Пример минимальной структуры:

  • tests/
  • pages/
  • utils/
  • conftest.py
  • requirements.txt
  • Импорт функций из модуля:

    Исключения: как падать правильно

    В тестировании важно различать ожидаемые и неожиданные ошибки.

    Практическое правило:

  • Если ошибка означает, что тест должен упасть, не прячьте её
  • Обрабатывайте только то, что вы действительно ожидаете и можете корректно восстановить
  • Логирование: чтобы разбирать падения быстрее

    print подходит для обучения, но в реальных тестах лучше logging.

    Документация: logging

    Инструменты тестировщика на Python

    Pytest

    pytest — де-факто стандарт для автотестов на Python.

    Что важно знать уже сейчас:

  • Тесты обычно лежат в файлах test_*.py
  • Тестовые функции обычно начинаются с test_
  • Для проверки ожиданий используют assert
  • Пример:

    Документация: pytest

    Отчёты (Allure)

    Allure часто используют для удобных отчётов по прогонам.

  • Документация: Allure Framework
  • Форматирование и качество кода

    Чтобы тесты были стабильными и одинаково читались командой:

  • Black — автоформатирование
  • Flake8 — базовые проверки стиля и ошибок
  • Эти инструменты особенно полезны, когда в тестах много повторяющихся паттернов и высокий темп изменений.

    Инструменты мобильной автоматизации (обзор)

    В этой статье мы не запускаем мобильные тесты, но фиксируем, что вам понадобится дальше.

    Appium

    Appium позволяет управлять мобильным приложением как пользователю: нажимать, вводить текст, свайпать.

  • Основной сайт: Appium
  • Python-клиент: Appium Python Client
  • ADB (Android Debug Bridge)

    ADB помогает работать с Android-устройством или эмулятором: ставить приложения, снимать логи, делать скриншоты.

  • Описание платформенных инструментов: Android SDK Platform-Tools
  • Минимальный чек-лист готовности к курсу

  • Установлен Python 3.11 или 3.12
  • Умеете создавать и активировать venv
  • Понимаете базовые типы и коллекции
  • Понимаете, зачем нужны функции, модули и исключения
  • Установили pytest в виртуальное окружение
  • В следующих материалах мы начнём собирать тестовый проект, подключать Appium и строить удобную архитектуру тестов для мобильного приложения.

    2. Мобильное тестирование: Android, iOS и виды автотестов

    Мобильное тестирование: Android, iOS и виды автотестов

    Как эта тема связана с Python-автотестами

    В предыдущей статье вы подготовили базу: Python, виртуальное окружение, pytest, понимание модулей, исключений и логирования. Теперь важно разобраться, что именно мы тестируем на мобильных платформах и какие бывают автотесты, чтобы дальше осознанно собрать стек (Appium + pytest) и выбрать правильную стратегию.

    Мобильная автоматизация почти всегда балансирует между тремя ограничениями:

  • Разнообразие устройств и версий ОС
  • Нестабильность UI (анимации, фоновые задачи, сеть)
  • Стоимость прогона (реальные девайсы, параллельность, CI)
  • !Общая архитектура того, как Python-тесты через Appium управляют Android и iOS

    Платформы: что важно знать про Android

    Android — экосистема с большим числом производителей, оболочек и конфигураций. Для автотестов это означает больше матрица устройств и выше риск различий поведения.

    Термины без "магии"

  • Приложение — устанавливаемый пакет (обычно apk; в релизах может встречаться aab, но на устройство в итоге попадает устанавливаемый вариант).
  • Эмулятор — виртуальное Android-устройство на компьютере.
  • ADB — консольный мост к устройству/эмулятору: установка приложений, сбор логов, выполнение команд.
  • Официально про ADB: Android SDK Platform-Tools

    Что чаще всего влияет на стабильность автотестов на Android

  • Разные размеры экранов и плотности (UI может "переезжать")
  • Разные версии Android и поведение разрешений
  • Производительские особенности (энергосбережение, агрессивное убийство фоновых процессов)
  • Системные диалоги (разрешения, обновления сервисов)
  • Инструменты UI-автоматизации в Android

    В Android мире часто встречаются два подхода:

  • UIAutomator (и его интеграции) — управление приложением с уровнем доступа к системному UI; в Appium это обычно backend UiAutomator2.
  • Espresso — нативный UI-фреймворк Android для тестов приложения (часто быстрее и стабильнее, но обычно требует участия разработки и теснее связан с кодовой базой).
  • Документация:

  • UI Automator
  • Espresso
  • Платформы: что важно знать про iOS

    iOS более однородна по устройствам и ОС, но предъявляет строгие требования к подписи, инфраструктуре и безопасности.

    Термины без "магии"

  • Приложение — пакет для установки на устройство (часто встречается ipa).
  • Simulator — симулятор iOS на macOS (это не эмулятор железа, он тесно интегрирован с Xcode).
  • Подпись (signing) — правила Apple, без которых приложение и инструменты автоматизации не запустятся на реальном устройстве.
  • Общее введение по тестированию UI на iOS: XCTest

    Как обычно устроена UI-автоматизация iOS

    Самый распространённый системный фреймворк — XCUITest. В Appium iOS-автоматизация обычно опирается на WebDriverAgent (компонент, который помогает управлять приложением через XCUITest).

    Ключевые практические отличия iOS:

  • Для реальных устройств почти всегда требуется корректная настройка подписи
  • Ограничения платформы сильнее влияют на доступ к внутренностям системы
  • Стабильность локаторов часто зависит от доступности (accessibility)
  • Документация Appium по iOS-драйверу: Appium XCUITest Driver

    Android и iOS: главные отличия для тестировщика

    | Область | Android | iOS | |---|---|---| | Инфраструктура | проще стартовать на большинстве ОС | для iOS обычно нужна macOS и Xcode | | Разнообразие устройств | очень высокое | ниже, но всё равно есть нюансы | | Системные диалоги | часто разные у производителей | более единообразные | | Инструменты вокруг устройства | ADB, logcat, много утилит | инструменты Xcode, системные ограничения | | Типичные проблемы | фрагментация, фоновые политики | signing, доступность, ограничения платформы |

    Вывод для стратегии: Android чаще требует шире матрицу устройств, iOS чаще требует сильнее подготовить инфраструктуру.

    Что именно мы автоматизируем в мобильных приложениях

    Мобильное приложение бывает не только "чисто нативным":

  • Нативное — интерфейс на нативных компонентах Android/iOS.
  • Гибридное — часть UI внутри WebView (встроенный браузер).
  • Мобильный веб — сайт, открываемый в браузере на телефоне.
  • Почему это важно:

  • Для WebView вам нужно уметь переключаться между контекстами (Native/Web).
  • Локаторы и поведение элементов могут отличаться.
  • Иногда часть сценария лучше закрыть API-тестом, а UI оставить только для критического пути.
  • Виды автотестов в мобильной разработке

    Ниже — практическая классификация, которую удобно использовать при планировании набора автотестов.

    Модульные (unit) тесты

  • Проверяют отдельные функции/классы внутри кода приложения.
  • Обычно пишутся разработчиками.
  • Дают самую быструю обратную связь.
  • Для тестировщика-автоматизатора на Python важно понимать роль: unit-тесты уменьшают количество багов, которые “утекут” в UI.

    Интеграционные тесты

  • Проверяют взаимодействие модулей: база данных, сеть, локальное хранилище.
  • Могут выполняться без полного UI.
  • На проектах часто выгодно уводить максимум проверок в этот слой, чтобы UI-автотестов было меньше.

    UI E2E (end-to-end) автотесты

  • Идут “глазами пользователя”: открыть экран, нажать кнопки, ввести текст, проверить результат.
  • Самые дорогие и самые хрупкие.
  • Именно этот тип чаще всего реализуют связкой pytest + Appium.
  • Примеры типичных E2E сценариев:

  • Логин и выход
  • Оформление заказа
  • Поиск и фильтры
  • Проверка, что критические экраны открываются
  • Smoke, Regression и "критический путь"

    Это не отдельные технологии, а способы организации набора тестов.

  • Smoke — короткий набор проверок “приложение живо” (часто запускают на каждый билд).
  • Regression — широкий набор проверок, чтобы убедиться, что ничего не сломали.
  • Critical path — сценарии, которые напрямую связаны с основными действиями пользователя и бизнес-ценностью.
  • Тесты доступности (accessibility)

  • Проверяют, что элементы корректно доступны для ассистивных технологий.
  • Практически полезны и для автоматизации, потому что хорошие accessibility-идентификаторы часто дают самые стабильные локаторы.
  • Обзор по accessibility на Android: Accessibility overview

    Тесты производительности и стабильности

  • Производительность: время запуска, время открытия экранов, лаги.
  • Стабильность: крэши, ANR (Android), утечки памяти.
  • Часто это отдельный трек с профильными инструментами, но UI-автотесты могут собирать артефакты для диагностики (логи, скриншоты, видео).

    !Пирамида тестирования, показывающая почему UI E2E тестов должно быть меньше

    Где запускать автотесты: эмуляторы, симуляторы и реальные устройства

    Эмулятор/симулятор

  • Дешевле и проще масштабировать (особенно в CI).
  • Хорошо подходит для smoke и большинства UI-проверок.
  • Может отличаться от реального мира (производительность, камера, push-уведомления, некоторые сенсоры).
  • Реальные устройства

  • Нужны для критичных проверок, завязанных на “железо” и окружение.
  • Полезны для проверки реальной производительности и поведения ОС.
  • Типичные причины выбрать реальное устройство:

  • Push-уведомления и их доставка
  • Камера, биометрия, NFC, Bluetooth
  • Реальные сетевые условия (прыгающий сигнал)
  • Практика проектирования UI-автотестов: что делает тест стабильным

    Эта часть напрямую связана с тем, как вы будете писать код на Python в следующих уроках.

    Локаторы: что выбирать в первую очередь

    Общее правило для мобильного UI: выбирайте локаторы, которые меньше всего зависят от верстки.

    Приоритет часто такой:

  • Accessibility ID (или аналогичный идентификатор доступности)
  • Уникальные resource-id (Android)
  • Текст (если он стабилен и не локализуется)
  • XPath как запасной вариант, когда другого нет
  • Ожидания и асинхронность

    Мобильный UI почти всегда асинхронен: анимации, загрузка данных, фоновые операции. Поэтому в автотестах важно:

  • Ждать событие (появление элемента), а не “спать” фиксированным временем
  • Собирать артефакты на падениях (скриншот, page source, логи)
  • В Python-стеке вы обычно реализуете это через явные ожидания и удобные обертки в Page Object.

    Что обязательно логировать

    Для разбора падений в CI полезно иметь минимум:

  • Скриншот на момент падения
  • Логи устройства (Android: logcat)
  • Исходники текущего UI (page source)
  • Метаданные окружения (платформа, версия ОС, модель устройства)
  • Что дальше по курсу

    После этой статьи у вас должна сложиться карта: Android и iOS отличаются инфраструктурой и инструментами, а UI E2E тесты — самые “дорогие”, поэтому их нужно проектировать аккуратно.

    Дальше мы перейдём к практическому стеку:

  • установка и запуск Appium
  • подключение Python-клиента Appium
  • создание первого проекта на pytest
  • базовые сценарии (запуск приложения, поиск элементов, клики, ввод текста)
  • Документация Appium: Appium Documentation

    3. Appium: настройка окружения и первый тест

    Appium: настройка окружения и первый тест

    Как эта тема продолжает курс

    В прошлых статьях вы подготовили базу по Python и инструментам тестировщика, а также разобрались, чем отличаются Android и iOS и какие бывают виды автотестов. Теперь переходим к практической части: настраиваем Appium, подключаем Python-клиент и пишем первый UI E2E тест на pytest.

    Цель этой статьи: чтобы у вас локально поднимался Appium-сервер, устройство (эмулятор/симулятор) было доступно, а Python-тест мог открыть приложение и выполнить простое действие.

    Что такое Appium и как он работает

    Appium в современном виде (Appium 2) — это сервер, который принимает команды управления приложением по протоколу WebDriver и передаёт их на конкретный драйвер платформы.

    Ключевые термины:

  • Appium Server — процесс, который слушает HTTP-запросы (обычно http://127.0.0.1:4723).
  • Driver — модуль Appium под конкретную платформу.
  • Клиент — библиотека в вашем языке (в курсе это Python-клиент), которая отправляет команды на сервер.
  • Сессия — подключение теста к конкретному устройству и приложению.
  • Capabilities — параметры, с которыми создаётся сессия: платформа, устройство, путь к приложению и другие настройки.
  • !Схема того, как тесты на Python управляют мобильным приложением через Appium

    Что нужно установить

    Ниже — практичный минимум для локального запуска.

    | Компонент | Зачем нужен | Где взять | |---|---|---| | Node.js (и npm) | установка и запуск Appium 2 | Node.js | | Appium 2 | сервер автоматизации | Appium Documentation | | Драйвер Android (UiAutomator2) | управление Android | Appium UiAutomator2 Driver | | Драйвер iOS (XCUITest) | управление iOS | Appium XCUITest Driver | | Android SDK Platform-Tools (adb) | связь с Android-устройством/эмулятором | Android SDK Platform-Tools | | Android Studio (для эмулятора) | создание и запуск Android-эмуляторов | Android Studio | | Python 3.11+ и pytest | тесты и раннер | pytest |

    Для iOS дополнительно:

  • Xcode (только macOS)
  • Симуляторы iOS
  • Установка Appium 2 и драйверов

    Установка Appium

    После установки Node.js проверьте:

    Установите Appium:

    Проверьте:

    Установка драйверов (обязательно для Appium 2)

    Appium 2 не включает драйверы по умолчанию: их нужно установить отдельно.

    Android (UiAutomator2):

    iOS (XCUITest):

    Посмотреть список установленных драйверов:

    Проверка окружения через appium-doctor

    appium-doctor помогает быстро найти недостающие зависимости.

    Установка:

    Проверка Android:

    Проверка iOS:

    Репозиторий: appium-doctor

    Подготовка Android окружения

    Android SDK и ADB

    Самый удобный путь — поставить Android Studio и установить SDK через него.

    Что важно для автоматизации:

  • наличие platform-tools (внутри них лежит adb)
  • наличие хотя бы одного эмулятора или подключённого реального устройства
  • Проверьте, что adb доступен:

    Проверьте устройства:

    Ожидаемо вы увидите хотя бы одну строку со статусом device (эмулятор или телефон).

    Если adb не находится, добавьте путь к platform-tools в переменную окружения PATH.

    Эмулятор Android

    Создайте эмулятор через Android Studio (AVD Manager) и запустите его. После запуска снова выполните:

    Подготовка iOS окружения (кратко)

    Для iOS вам нужна macOS и Xcode.

    Минимальный старт для обучения:

  • поставить Xcode
  • запустить iOS Simulator
  • использовать драйвер XCUITest
  • Реальные устройства iOS обычно требуют дополнительной настройки подписи (signing) и инфраструктуры WebDriverAgent, поэтому для первого шага проще начинать с симулятора.

    Appium Inspector: как подобрать локаторы

    Перед тем как писать тесты, полезно уметь быстро находить элементы и проверять локаторы.

    Инструмент: Appium Inspector.

  • Скачать: Appium Inspector
  • Зачем он нужен:

  • увидеть дерево UI
  • проверить, что у элемента есть стабильный идентификатор (например, accessibility id)
  • протестировать поиск элемента и действия вручную до написания кода
  • Создаём Python-проект для мобильных тестов

    Структура проекта (минимальная):

  • tests/
  • conftest.py
  • requirements.txt
  • Установка зависимостей в вашем venv:

    Документация клиента: Appium Python Client

    Пояснение: Python-клиент Appium использует API WebDriver и включает нужные компоненты для работы с Appium-сервером.

    Первый запуск: поднимаем Appium Server

    В отдельном терминале запустите:

    По умолчанию сервер стартует на http://127.0.0.1:4723.

    Если порт занят, можно запустить на другом:

    Первое приложение для тренировки

    Чтобы не зависеть от вашего рабочего приложения, удобно взять учебное приложение, которое специально сделано для автотестов.

    Рекомендуемый вариант: The App от команды Appium.

  • Репозиторий и сборки: The App
  • Скачайте apk (для Android) или app/ipa (для iOS) из релизов и сохраните в проект, например в папку apps/.

    Первый тест на pytest: открыть приложение и нажать кнопку

    Ниже пример для Android эмулятора и драйвера UiAutomator2.

    Фикстура драйвера

    conftest.py:

    Что здесь происходит:

  • platformName говорит Appium, какая платформа нужна
  • appium:automationName выбирает драйвер автоматизации (для Android это чаще всего UiAutomator2)
  • appium:deviceName помогает выбрать устройство (для эмулятора обычно достаточно этого значения)
  • appium:app указывает путь к файлу приложения
  • Тест

    tests/test_smoke.py:

    Важно:

  • AppiumBy.ACCESSIBILITY_ID обычно самый стабильный тип локатора
  • если у вас не находятся элементы, откройте приложение в Appium Inspector и проверьте реальные значения accessibility id в вашей версии сборки
  • Запуск

    В терминале проекта:

    Если всё настроено правильно, вы увидите успешный прогон, а на эмуляторе будет видно, как тест открыл экран логина.

    Частые проблемы и быстрые проверки

  • Appium запущен, но тест не подключается: проверьте URL и порт (http://127.0.0.1:4723) и что сервер действительно работает.
  • Ошибка про отсутствующий драйвер: проверьте appium driver list --installed и при необходимости выполните appium driver install uiautomator2.
  • adb не найден: проверьте установку platform-tools и переменную PATH.
  • Эмулятор не виден: проверьте adb devices.
  • Элемент не находится: сначала подтвердите локатор в Appium Inspector, затем переносите его в код.
  • Что дальше

    Теперь у вас есть рабочая цепочка: pytest → Python-клиент → Appium Server → драйвер платформы → устройство.

    Следующий шаг курса обычно включает:

  • явные ожидания вместо мгновенного поиска элементов
  • более удобную архитектуру тестов (Page Object)
  • сбор артефактов (скриншоты, page source, логи) при падениях
  • параметризацию под Android и iOS
  • 4. pytest для мобильных автотестов: фикстуры, марки и отчёты

    pytest для мобильных автотестов: фикстуры, марки и отчёты

    Как эта тема продолжает курс

    В прошлой статье вы подняли Appium Server, подключили Python-клиент и написали первый тест, который открывает экран и нажимает кнопку. Следующий шаг, чтобы проект стал похож на рабочий, — правильно организовать:

  • фикстуры для драйвера и тестовых данных
  • марки для управления наборами тестов (smoke, regression, Android/iOS)
  • отчёты и артефакты, чтобы разбирать падения в CI
  • Эта статья показывает практичные паттерны pytest, которые чаще всего используют в мобильной UI-автоматизации связкой pytest + Appium.

    !Схема показывает, как pytest через фикстуры создаёт Appium-сессию и управляет устройством

    Минимальная структура проекта на pytest для мобильных тестов

    Типовая минимальная раскладка:

  • tests/ — тесты
  • pages/ — Page Object (появится/расширится позже)
  • utils/ — утилиты (логи, ожидания, генераторы данных)
  • conftest.py — фикстуры и хуки pytest
  • pytest.ini — регистрация марок и настройки
  • Почему conftest.py важен:

  • он автоматически подхватывается pytest без импортов
  • в нём удобно держать драйвер, конфиг и общие фикстуры
  • Документация: pytest fixtures

    Фикстуры: база стабильного запуска Appium

    Фикстура в pytest — это функция, которая подготавливает состояние для теста и отдаёт его как аргумент.

    В мобильной автоматизации фикстуры обычно отвечают за:

  • создание и закрытие Appium-сессии (driver)
  • подготовку тестовых данных
  • навигацию в нужное состояние (например, авторизованный пользователь)
  • сбор артефактов при падении
  • Базовая фикстура драйвера с yield

    Паттерн yield позволяет гарантированно выполнить teardown: закрыть сессию даже если тест упал.

    conftest.py:

    Ключевая идея:

  • всё, что до yield, — подготовка
  • всё, что после yield, — освобождение ресурсов
  • Области видимости фикстур: function, class, module, session

    У фикстуры есть scope — как часто её создавать.

    Таблица практического выбора:

    | scope | Когда создаётся | Когда использовать в мобильных тестах | |---|---|---| | function | на каждый тест | самый безопасный вариант, меньше флаков из-за "грязного" состояния | | class | на класс тестов | если тесты в классе идут строго последовательно и делят один сценарий | | module | на файл | иногда для ускорения, но риск влияния тестов друг на друга | | session | один раз на прогон | обычно не рекомендуют для UI: сессия может деградировать и копить проблемы |

    Пример фикстуры драйвера на function явно:

    Практическое правило для UI E2E:

  • начинайте с scope="function"
  • оптимизацию по скорости делайте только после появления стабильности и метрик времени прогона
  • Цепочки фикстур: логин как отдельная фикстура

    Часто нужен авторизованный пользователь. Вместо копирования шагов создают фикстуру, которая использует driver.

    Тест:

    Важно:

  • фикстура должна быть короткой и предсказуемой
  • сложную навигацию лучше вынести в Page Object (позже по курсу)
  • Параметризация платформы: Android и iOS через один тестовый набор

    Один из удобных подходов — добавлять опцию командной строки --platform и на её основе строить capabilities.

    conftest.py:

    Запуск:

    Замечания:

  • iOS-указанные значения примерные: имя симулятора и формат приложения зависят от вашей сборки
  • платформа iOS обычно требует macOS и Xcode, как обсуждали в статье про Android/iOS
  • Документация: pytest custom command line options

    Марки: управление наборами тестов (smoke, regression, android, ios)

    Марка в pytest — это метка на тесте, по которой его можно включать или исключать из прогона.

    В мобильных проектах марки обычно отвечают за:

  • что запускать (smoke, regression)
  • где запускать (android, ios)
  • какой тип теста (ui, api)
  • Документация: pytest markers

    Регистрация марок в pytest.ini

    Чтобы pytest не ругался предупреждениями и чтобы набор марок был явным, зарегистрируйте их.

    pytest.ini:

    Использование марок в тестах

    Запуск по маркам

    Примеры команд:

    Практический совет:

  • держите smoke маленьким, чтобы его реально гонять часто
  • regression расширяйте постепенно, следя за временем прогона
  • skip и xfail для платформенных различий

    В мобильной автоматизации часть сценариев может быть временно нестабильной или неактуальной для платформы.

    skip — тест не выполняется.

    xfail — тест ожидаемо падает (например, известный баг). Удобно, чтобы видеть динамику, но не красить прогон в красный.

    Документация: pytest skip and xfail

    Отчёты и артефакты: что нужно для разбора падений

    Один из главных минусов UI E2E тестов — они дорогие и иногда падают нестабильно. Поэтому отчётность и артефакты критичны.

    Минимальный набор артефактов для мобильного UI падения:

  • скриншот
  • page source (дерево UI)
  • логи (как минимум логи теста; для Android часто ещё logcat)
  • JUnit XML: базовый отчёт для CI

    Многие CI-системы умеют отображать JUnit XML из коробки.

    Запуск:

    Документация: pytest JUnitXML

    Allure: удобный отчёт с вложениями

    Allure часто используют для тест-отчётов в автотестах: он показывает шаги, метки, окружение и умеет хранить вложения.

    Ссылки:

  • Allure Framework
  • allure-pytest
  • Установка зависимостей Python:

    Запуск с результатами:

    Генерация отчёта (нужна установленная утилита Allure на машине):

    Автосбор скриншота и page source на падении через хук pytest

    Ниже практичный хук, который сохраняет артефакты в папку и, если подключён Allure, прикрепляет их к отчёту.

    conftest.py:

    Что здесь важно понимать:

  • pytest_runtest_makereport вызывается для каждого теста и каждой фазы (setup, call, teardown)
  • артефакты логичнее собирать на фазе call, когда упали проверки сценария
  • item.funcargs.get("driver") достаёт созданный фикстурой драйвер, если он реально был в тесте
  • Документация по хукам: pytest hook reference

    Практические правила для мобильного pytest-проекта

    Чтобы набор тестов оставался управляемым и не превращался в хаос:

  • делайте фикстуры короткими и конкретными
  • используйте yield-фикстуры для гарантированного закрытия Appium-сессии
  • регистрируйте марки в pytest.ini, а не держите их "в голове"
  • начинайте со scope="function" для драйвера
  • подключайте хотя бы один отчётный формат для CI (--junitxml или Allure)
  • собирайте артефакты на падениях сразу, иначе разбор флаков будет слишком дорогим
  • Что дальше

    Теперь у вас есть основа тестового фреймворка на pytest: фикстуры для драйвера и состояний, марки для управления наборами и отчётность для диагностики.

    Следующий логичный шаг (и типичное продолжение курса) — сделать тесты удобнее и стабильнее:

  • явные ожидания вместо прямого find_element
  • Page Object для экранов
  • единые обёртки для действий и проверок
  • запуск в CI и базовая параллелизация на уровне матрицы устройств
  • 5. Page Object и архитектура автотестов для мобильных приложений

    Page Object и архитектура автотестов для мобильных приложений

    Зачем Page Object в мобильной автоматизации

    В предыдущих статьях вы подняли Appium, научились создавать driver через фикстуры pytest, размечать тесты марками и собирать артефакты на падениях. Следующая практическая проблема, с которой сталкиваются почти все проекты: тесты начинают быстро разрастаться, а повторяющиеся шаги (поиск элементов, клики, ожидания, навигация) размазываются по файлам.

    Page Object решает эту проблему: он отделяет логику работы со страницей/экраном от логики теста. В итоге:

  • тесты становятся короче и читаемее
  • локаторы централизуются
  • ожидания и ретраи можно унифицировать
  • изменения в UI чаще правятся в одном месте
  • Классическое описание паттерна: PageObject (Martin Fowler)

    !Поясняет разделение на слои и место Page Object

    Базовая архитектура проекта

    Цель архитектуры в автотестах не в “идеальной красоте”, а в том, чтобы:

  • быстро добавлять новые проверки
  • дешево чинить падения
  • локализовать изменения UI
  • Практичная стартовая структура для pytest + Appium:

  • tests/
  • pages/
  • pages/components/
  • utils/
  • conftest.py
  • pytest.ini
  • Где что лежит:

  • tests/ содержит только сценарии и проверки (assert)
  • pages/ содержит классы экранов (Page Object)
  • pages/components/ содержит переиспользуемые части UI (например, нижнее меню)
  • utils/ содержит ожидания, логирование, хелперы локаторов, конфиг
  • Что такое Page Object на практике

    Page Object — это класс, который предоставляет методы уровня “действий пользователя” и скрывает детали UI.

    Типичный набор ответственности Page Object:

  • хранить локаторы элементов экрана
  • уметь открыть экран (если это возможно)
  • выполнять действия (нажать, ввести, свайпнуть)
  • возвращать данные для проверок (текст, состояние видимости)
  • Что лучше не делать внутри Page Object:

  • превращать его в “мини-тест” с большим количеством assert
  • смешивать несколько экранов в одном классе
  • реализовывать сложную бизнес-логику, не связанную с UI
  • Практическое правило:

  • тест отвечает на вопрос “что проверяем?”
  • Page Object отвечает на вопрос “как взаимодействовать с экраном?”
  • Почему мобильные тесты особенно нуждаются в Page Object

    У мобильного UI есть особенности, из-за которых “прямой find_element в тестах” быстро становится дорогим:

  • анимации и асинхронные обновления (элемент может появляться не сразу)
  • разные локаторы на Android и iOS
  • системные диалоги (разрешения) и нестабильное окружение
  • необходимость собирать артефакты и логи при падении
  • Page Object позволяет спрятать в одном месте:

  • явные ожидания
  • кроссплатформенные локаторы
  • единый стиль логирования действий
  • Локаторы для мобильных экранов

    В прошлой части курса вы уже использовали AppiumBy.ACCESSIBILITY_ID. Для Page Object важно зафиксировать правило выбора локаторов.

    Приоритет локаторов обычно такой:

  • ACCESSIBILITY_ID
  • Android ID (обычно resource-id)
  • iOS IOS_PREDICATE (когда нет стабильных id)
  • текст (если он стабилен и не локализуется)
  • XPATH как последний вариант
  • Справка по стратегиям поиска в Appium для Python находится в документации клиента: Appium Python Client

    Явные ожидания как часть архитектуры

    В мобильном UI “мгновенный поиск” часто даёт флаки: тест то проходит, то падает, потому что элемент появляется через доли секунды.

    Вместо time.sleep() используют явные ожидания.

    Ожидания в экосистеме Appium обычно реализуются через Selenium WebDriverWait, потому что Appium совместим с WebDriver API. Концепция ожиданий: Selenium waits

    Идея, которую удобно заложить в архитектуру:

  • тесты и Page Object не должны напрямую делать “голый” find_element
  • вместо этого используется единый метод find() с ожиданием
  • Базовый класс BasePage

    BasePage централизует общие действия: найти элемент, кликнуть, ввести текст, получить текст, дождаться видимости.

    Пример минимального BasePage:

    Что здесь важно:

  • Locator — это пара (strategy, value), например (AppiumBy.ACCESSIBILITY_ID, "Login")
  • wait_visible реализует единый стиль ожидания
  • методы click и type используют ожидание и поэтому более стабильны
  • Page Object для экрана логина

    Ниже пример экрана логина, который использует BasePage.

    Особенность хорошего Page Object в том, что методы звучат как действия пользователя: login(), open_settings(), add_to_cart().

    Навигация и “возврат следующих страниц”

    Частый вопрос в архитектуре: должен ли login() возвращать новый Page Object.

    Оба подхода встречаются:

  • login() возвращает HomePage, если это действительно переход на новый экран
  • login() ничего не возвращает, а тест сам создаёт нужную страницу и проверяет её
  • Практичное правило для старта:

  • возвращайте новый Page Object только если переход гарантирован и стабилен
  • если после действия возможны разные исходы (ошибка или успех), лучше сделать отдельные методы проверки результата
  • Пример варианта с возвратом:

    Компоненты: когда один экран состоит из переиспользуемых частей

    В мобильных приложениях часто есть общий компонент, который присутствует на многих экранах:

  • нижняя навигация
  • верхняя панель
  • поиск
  • Если компонент повторяется, его выносят в pages/components/, чтобы не дублировать локаторы и методы.

    Пример “нижнего меню”:

    Тогда любой экран может “содержать” компонент:

    Кроссплатформенность: Android и iOS в одном Page Object

    Если вы параметризуете платформу через --platform (как в прошлой статье), возникает вопрос: где хранить различия локаторов.

    Есть два популярных подхода.

    Разные классы для разных платформ

  • LoginPageAndroid
  • LoginPageIOS
  • Плюсы:

  • максимально явно
  • легко держать разные локаторы и разные особенности
  • Минусы:

  • больше кода и файлов
  • Один класс и “локаторы по платформе”

    Плюсы:

  • меньше сущностей
  • Минусы:

  • внутри класса появляются ветвления
  • Для старта часто достаточно простого словаря локаторов, а затем, когда различий станет много, перейти на раздельные классы.

    Пример локатора “по платформе”:

    Чтобы это работало удобно, платформу обычно передают из фикстуры.

    Интеграция Page Object с фикстурами pytest

    Вы уже создавали driver в conftest.py. Следующий шаг: отдавать тестам не “голый драйвер”, а страницы.

    Пример фикстуры, которая создаёт LoginPage:

    Теперь тест выглядит как сценарий:

    Где держать assert: в тесте или в Page Object

    Чтобы тесты были управляемыми, удобно придерживаться таких правил:

  • простые технические проверки (элемент видим) допустимы внутри Page Object как методы is_opened()
  • бизнес-проверки обычно остаются в тесте, чтобы было видно цель сценария
  • Пример “правильной читаемости”:

    Антипаттерны Page Object в мобильной автоматизации

    Ниже — частые ошибки, из-за которых Page Object перестаёт помогать.

  • Слишком умные Page Object: внутри много ветвлений, условий, ретраев, и они превращаются в “скрытые тесты”.
  • Смешивание разных экранов: один класс отвечает сразу за логин, настройки и профиль.
  • Дублирование ожиданий: часть методов ждёт видимость, часть кликает без ожидания.
  • Хрупкие локаторы по умолчанию: массовое использование XPath без крайней необходимости.
  • Минимальные правила качества для архитектуры

    Чтобы Page Object действительно улучшал проект, зафиксируйте правила и применяйте их последовательно.

  • Все действия с элементами идут через методы BasePage с ожиданиями.
  • Локаторы не лежат в тестах.
  • Повторяющиеся куски UI выносятся в компоненты.
  • Любой Page Object умеет ответить, “открыт ли экран” (например, is_opened()).
  • Различия Android/iOS имеют явную стратегию: либо раздельные классы, либо централизованная мапа локаторов.
  • Что дальше по курсу

    С Page Object вы получили каркас, на который удобно наращивать проект:

  • единые ожидания и стабильные действия
  • централизованные локаторы
  • понятные тесты-сценарии
  • Следующий логичный шаг после архитектуры:

  • расширить набор ожиданий (кликабельность, исчезновение, наличие текста)
  • добавить обёртки для жестов (свайпы, скролл)
  • стандартизировать сбор артефактов на уровне Page Object и хуков pytest
  • подготовить запуск в CI и постепенное масштабирование матрицы устройств
  • 6. Стабильность автотестов: ожидания, локаторы, flakiness и отладка

    Стабильность автотестов: ожидания, локаторы, flakiness и отладка

    Зачем эта тема в курсе

    В прошлых статьях вы собрали рабочую цепочку pytest → Appium → устройство и начали выстраивать архитектуру через Page Object и единые ожидания. На практике следующий “болевой” этап почти у всех одинаковый: тесты начинают иногда падать без реальных багов.

    Такое поведение называют flakiness: тест нестабилен и даёт ложные падения. В мобильной UI-автоматизации это особенно распространено из-за асинхронности интерфейса, анимаций, фоновых задач, сети и особенностей устройств.

    Цель этой статьи:

  • научиться выбирать правильные ожидания вместо sleep
  • делать локаторы устойчивыми к изменениям UI
  • понимать типовые причины flakiness и как их лечить
  • собирать достаточно данных для быстрой отладки падений
  • !Схема слоёв и типовых мест, где появляется нестабильность

    Что такое flakiness и как отличить её от настоящего бага

    Flaky-тест может падать по причинам, не связанным с дефектом в приложении:

  • элемент появился чуть позже из-за анимации или сети
  • локатор нашёл “не тот” элемент
  • открылась системная модалка (разрешения, уведомления)
  • приложение было в неправильном состоянии (не сбросили данные, остался логин)
  • Практичные признаки flakiness:

  • повторный запуск без изменений часто проходит
  • падения “прыгают” по разным местам сценария
  • падения зависят от скорости машины, нагрузки CI, версии устройства
  • Важно: flaky-тест всё равно должен считаться проблемой качества автотестов, иначе он быстро обесценит прогон.

    Ожидания: главный инструмент борьбы с нестабильностью

    Мобильный UI редко синхронен: экран открылся, но элементы догружаются; кнопка видна, но ещё не кликабельна; запрос ушёл, но ответ не пришёл. Поэтому тесту нужен способ “подождать правильное состояние”.

    Рекомендуемая база по ожиданиям: WebDriver waits (Selenium)

    Почему time.sleep() почти всегда вреден

    time.sleep(2) делает тест:

  • медленнее на быстрых средах
  • всё равно нестабильным на медленных средах
  • хуже диагностируемым (неясно, что именно ждём)
  • Единственный разумный сценарий для sleep в UI-тестах: очень редкие технические обходы, когда нет события, которое можно дождаться. Даже в этом случае лучше попытаться заменить на ожидание условия.

    Виды ожиданий в Appium-проектах на Python

    В связке Appium + Python чаще всего встречаются:

  • Implicit wait (неявное ожидание): драйвер будет ждать элемент при каждом find_element.
  • Explicit wait (явное ожидание): вы явно ждёте конкретное условие (видимость, кликабельность, исчезновение).
  • Практическое правило для мобильной автоматизации:

  • держите implicit wait выключенным или минимальным
  • используйте explicit wait как стандартный подход
  • Причина: implicit wait может “размазывать” задержки по всем операциям и усложнять разбор таймаутов.

    Какие условия ждать на мобильном UI

    Чаще всего вам нужны не “найти элемент”, а дождаться конкретного состояния:

  • элемент присутствует в дереве UI
  • элемент видим
  • элемент кликабелен
  • элемент исчез (например, лоадер)
  • у элемента появился нужный текст или атрибут
  • Пример расширения BasePage ожиданиями (продолжение архитектуры из прошлой статьи):

    Таймауты: как выбрать “нормальные” значения

    Слишком маленькие таймауты дают flakiness. Слишком большие таймауты превращают падения в долгие “подвисания”.

    Практичная стратегия:

  • базовый таймаут для большинства UI-операций: 10–15 секунд
  • отдельные, явно названные ожидания для долгих операций (например, логин, первая загрузка)
  • фиксируйте таймауты в одном месте (например, utils/timeouts.py), чтобы команда не “размазывала” магические числа
  • Частая ошибка: ждать видимость, когда элемент уже видим, но перекрыт

    На мобильном UI элемент может быть видимым, но не кликабельным:

  • поверх него лоадер
  • поверх открыта клавиатура
  • идёт анимация перехода
  • Поэтому клик лучше строить через wait_clickable, а не через wait_visible.

    Локаторы: как сделать поиск элементов устойчивым

    Локатор — это ваш “контракт” с UI. Flakiness очень часто начинается с локатора: тест нажал не туда, нашёл другой элемент, или не нашёл элемент из-за изменения структуры.

    Приоритет стратегий локаторов в мобильных приложениях

    Обычно порядок предпочтения такой:

  • ACCESSIBILITY_ID (лучший вариант для стабильности)
  • Android ID (обычно resource-id)
  • iOS IOS_PREDICATE или CLASS_CHAIN (когда нет стабильных id)
  • текст (только если текст стабилен и не локализуется)
  • XPATH как последний вариант
  • Инструмент для проверки локаторов: Appium Inspector

    Почему Accessibility ID почти всегда лучший выбор

    Accessibility-идентификатор:

  • чаще всего уникален и осознанно задан
  • меньше зависит от верстки и вложенности
  • одновременно полезен для доступности (это повышает шанс, что команда будет поддерживать его стабильным)
  • Практика для команды: договориться с разработкой, что ключевые элементы имеют стабильные accessibility id.

    Когда XPath неизбежен и как уменьшить ущерб

    XPath становится хрупким, потому что зависит от структуры дерева UI. Если без него никак:

  • избегайте абсолютных путей вида /hierarchy/.../android.widget...
  • старайтесь привязаться к стабильным атрибутам (content-desc, resource-id, name)
  • делайте XPath как можно короче и более “семантичным”
  • “Один локатор на два элемента” — скрытый источник флаков

    Если локатор находит несколько элементов, Appium может вернуть “первый попавшийся”. На разных устройствах и версиях ОС порядок может отличаться.

    Правило:

  • локатор должен быть максимально уникальным
  • если элементов несколько по дизайну (например, список), выбирайте элемент по индексу только после стабилизации сценария и понимания, что индекс детерминирован
  • Мобильные причины flakiness и способы лечения

    Ниже — типовые причины нестабильности именно в мобильном UI.

    Анимации и переходы

    Симптомы:

  • элемент найден, но click не срабатывает
  • ошибка Element not interactable или похожие
  • Лечение:

  • ожидать кликабельность
  • ждать исчезновение лоадеров и оверлеев
  • уменьшать длительность анимаций настройками приложения, если это возможно в тестовой сборке
  • Клавиатура перекрывает элементы

    Симптомы:

  • кнопка “Login” не нажимается после ввода
  • Лечение:

  • явный шаг скрытия клавиатуры (если платформа/приложение это поддерживает)
  • кликать по области вне инпута, если это часть UX
  • в Page Object держать это как часть метода ввода/логина, а не размазывать по тестам
  • Системные диалоги и разрешения

    Симптомы:

  • на чистом устройстве тест падает на первом запуске
  • Лечение:

  • предусмотреть обработку разрешений как отдельный компонент/экран
  • использовать настройки capabilities, которые помогают автопринятию разрешений (там, где это уместно), но помнить, что поведение может различаться
  • Данные и состояние приложения

    Симптомы:

  • тест иногда стартует “не с того экрана”
  • Лечение:

  • в фикстуре драйвера обеспечивать предсказуемый старт: чистые данные, нужная активити/экран
  • не зависеть от результатов прошлых тестов
  • если тест требует сложного состояния, создавайте его через API/бэкенд (где возможно), а UI оставляйте для критического пути
  • Сеть и нестабильный бекенд

    Симптомы:

  • разные падения на загрузке списков, таймауты
  • Лечение:

  • отделять UI-проверку от проверки данных: “экран открылся и элементы доступны” и “данные корректны”
  • использовать тестовые стенды с предсказуемыми данными
  • логировать сетевые ошибки на уровне приложения (если есть доступ) и собирать логи устройства
  • Ретраи: когда можно, а когда нельзя

    Иногда команды добавляют “перезапуск упавших тестов” как костыль. В pytest для этого есть плагин: pytest-rerunfailures

    Пример запуска:

    Правильное отношение к ретраям:

  • ретраи можно использовать как временную страховку для CI
  • ретраи не должны заменять исправление ожиданий, локаторов и состояния
  • Практика: если тест “лечится” ретраем, заведите задачу на стабилизацию и добавьте диагностику, иначе вы просто спрячете проблему.

    Отладка падений: что собирать и как быстро локализовать причину

    Когда тест упал, вам нужно быстро ответить на два вопроса:

  • это баг в приложении или flakiness?
  • если flakiness, то в чём причина: ожидание, локатор, состояние, окружение?
  • Минимальный набор артефактов на падении

    Минимум, который реально ускоряет разбор:

  • скриншот
  • page source (дерево UI)
  • логи теста
  • Это вы уже частично делали через pytest_runtest_makereport в прошлой статье.

    Логи устройства

    Для Android очень часто помогает logcat:

  • документация: Logcat (Android)
  • Быстрые команды для ручной диагностики:

    Практика: если тест упал “в UI”, а на скриншоте всё выглядит нормально, logcat часто показывает крэш, проблемы рендера, сетевые ошибки, ошибки авторизации.

    Логи Appium Server

    Если вы запускаете Appium локально в отдельном терминале, вы уже видите лог команд. Для CI полезно сохранять Appium-логи в файл.

    Идея для диагностики:

  • если элемент не найден, в логах Appium обычно видно, каким локатором искали и сколько времени заняло ожидание
  • Appium Inspector как инструмент воспроизведения

    Если тест не находит элемент, не пытайтесь чинить “вслепую”. Откройте экран в Appium Inspector и:

  • проверьте, что локатор реально существует в текущей сборке
  • проверьте, не меняется ли дерево UI после действий
  • убедитесь, что вы в нужном контексте (Native/WebView), если приложение гибридное
  • Техника “сужения проблемы”

    Чтобы быстрее понять причину, действуйте так:

  • Перезапустите один упавший тест изолированно.
  • Если падает только в наборе, проверьте зависимость от состояния (данные, авторизация, порядок тестов).
  • Если падает только на конкретном устройстве/ОС, сравните локаторы и системные диалоги.
  • Если падение связано со временем, замените find_element на ожидание конкретного условия.
  • Стандартизация логов действий в Page Object

    Flaky-падение легче чинить, когда в логах видно шаги.

    Практика: логируйте действия на уровне BasePage.

    В связке с Allure это особенно удобно, потому что в отчёте видно последовательность действий.

    Документация плагина: allure-pytest

    Чек-лист стабилизации одного теста

    Если тест нестабилен, пройдите короткий чек-лист:

  • Локатор уникален и основан на ACCESSIBILITY_ID или ID, а не на структуре.
  • Действия выполняются через методы Page Object с explicit waits.
  • Нет sleep, кроме редких технических обходов.
  • На падении сохраняются скриншот и page source.
  • Тест не зависит от результатов других тестов и запускается на “чистом” состоянии.
  • Что дальше

    После стабилизации ожиданий и локаторов следующий шаг в развитии фреймворка обычно такой:

  • обёртки для жестов (свайп, скролл) с предсказуемыми ожиданиями
  • более сильная работа с состоянием (быстрый логин, подготовка данных)
  • запуск в CI с артефактами и разумной матрицей устройств
  • Для этого фундамент уже есть: pytest-фикстуры, хуки для артефактов и архитектура Page Object.

    7. CI/CD и запуск на фермах устройств: Jenkins/GitHub Actions, Allure

    CI/CD и запуск на фермах устройств: Jenkins/GitHub Actions, Allure

    Как эта тема продолжает курс

    Ранее вы собрали локальный стек pytest + Appium, настроили фикстуры, марки, артефакты на падениях, Page Object и подходы к стабилизации. Следующий шаг, без которого мобильная автоматизация редко приносит пользу команде, — встроить тесты в CI/CD и научиться запускать их:

  • на каждом изменении кода (пул-реквесты, ветки)
  • на разных устройствах и версиях ОС (матрица)
  • с отчётами и артефактами (Allure, JUnit XML)
  • В мобильных проектах “просто запустить в CI” означает решить два практических вопроса:

  • где взять устройство или окружение (эмулятор/симулятор или ферма устройств)
  • как собрать и сохранить диагностику, чтобы быстро разбирать падения
  • !Общая картина пайплайна и где находятся Appium, устройства и отчёты

    Что такое CI/CD в контексте автотестов

    CI (Continuous Integration) — регулярный автоматический запуск проверок после изменений в репозитории.

    CD (Continuous Delivery/Deployment) — автоматическая доставка сборки дальше: на тестовые стенды, в стор, в internal distribution.

    Для мобильной автоматизации обычно строят пайплайн, который:

  • получает приложение (apk, ipa, .app) из сборки
  • запускает UI E2E тесты
  • сохраняет результаты и артефакты
  • публикует отчёты
  • Практичный минимум артефактов для мобильного UI падения:

  • скриншот
  • page source
  • логи (тестовые и, по возможности, серверные Appium и логи устройства)
  • Где запускать мобильные UI тесты в CI

    Есть две базовые стратегии.

    Локально в CI на эмуляторе или симуляторе

    Плюсы:

  • дешевле
  • быстрый старт
  • проще контролировать окружение
  • Минусы:

  • сложнее добиться полного соответствия реальным устройствам
  • инфраструктура iOS требует macOS и Xcode
  • На ферме устройств (Device Farm)

    Ферма устройств — это сервис, который даёт:

  • реальные девайсы или управляемые эмуляторы
  • готовый удалённый Appium endpoint
  • параллельные сессии
  • часто — видео, логи, скриншоты “из коробки”
  • Плюсы:

  • реальное железо и ОС
  • проще масштабировать матрицу устройств
  • легче параллелить
  • Минусы:

  • стоимость
  • нужно управлять секретами и лимитами параллельности
  • часть диагностики хранится “вне” вашего CI (важно забирать артефакты)
  • Сводная таблица выбора:

    | Критерий | Эмулятор/симулятор в CI | Ферма устройств | |---|---|---| | Стоимость | ниже | выше | | Матрица устройств | ограничена | широкая | | Параллельность | сложнее (ресурсы раннера) | обычно проще | | Соответствие “реальному миру” | среднее | выше | | Скорость старта | высокая | средняя (загрузка приложения, очередь) |

    GitHub Actions: практичный стартовый пайплайн

    Документация: GitHub Actions

    Ниже пример пайплайна для Android UI тестов на эмуляторе. Он показывает ключевые этапы:

  • поднять Python и зависимости
  • запустить Appium server
  • поднять Android эмулятор
  • запустить pytest с Allure и JUnit XML
  • загрузить артефакты в GitHub
  • /.github/workflows/mobile-ui.yml:

    Что важно понимать:

  • --alluredir сохраняет сырые результаты Allure, а не готовый HTML
  • --junitxml полезен, потому что многие CI-системы умеют показывать JUnit отчёты
  • if: always() гарантирует, что отчёты загрузятся даже при падении тестов
  • Ссылки по использованным действиям:

  • setup-python
  • setup-node
  • android-emulator-runner
  • upload-artifact
  • Jenkins: типовой Declarative Pipeline

    Jenkins часто используют, когда инфраструктура “внутри периметра” и нужны свои агенты (например, выделенные macOS-ноды под iOS).

    Документация: Jenkins

    Минимальный Jenkinsfile для прогона pytest и публикации артефактов:

    Практические замечания для Jenkins:

  • для iOS обычно выделяют отдельные macOS-агенты с установленным Xcode
  • Appium и SDK лучше ставить на агент заранее, чтобы не тратить время каждого прогона
  • Allure в CI: результаты, отчёты и “прикладывания” артефактов

    Ссылки:

  • Allure Framework
  • allure-pytest
  • Как запускать pytest, чтобы собирать Allure

    Минимальная команда:

    Если вы ранее добавили хук pytest_runtest_makereport и прикладываете скриншот и page source, то в reports/allure-results будут лежать:

  • результаты тестов
  • вложения (если вы их прикрепляете через allure.attach)
  • Где хранить и как публиковать Allure

    Есть два распространённых способа.

  • Хранить allure-results как артефакт CI (просто и надёжно)
  • Генерировать HTML отчёт (allure-report) и публиковать
  • Генерация отчёта в CI обычно выглядит так:

    Но это требует доступного Allure CLI на раннере. Подробности установки зависят от вашей инфраструктуры и политики.

    Полезная практика: фиксировать окружение в Allure

    Allure умеет показывать данные окружения, если рядом с результатами положить файл environment.properties.

    Пример (создание файла перед запуском тестов):

    В реальном проекте туда часто добавляют:

  • версию приложения
  • номер билда
  • модель устройства или тип раннера
  • Запуск на фермах устройств через удалённый Appium

    Ключевая идея: ваши тесты на Python почти не меняются. Меняется место, куда они подключаются:

  • локально: http://127.0.0.1:4723
  • ферма: удалённый Appium endpoint, который выдаёт сервис
  • Типовой алгоритм работы с фермой устройств:

  • Загрузить приложение (или указать ссылку на сборку)
  • Получить идентификатор приложения в сервисе
  • Описать capabilities (платформа, устройство, версия ОС, app id)
  • Запустить тесты на удалённый URL
  • Забрать артефакты из CI и, при необходимости, из UI фермы
  • Популярные варианты (ссылки на официальные страницы):

    | Сервис | Что даёт | Где смотреть | |---|---|---| | BrowserStack App Automate | реальные устройства, удалённый Appium | BrowserStack App Automate | | Sauce Labs Mobile | реальные устройства, Appium, видео/логи | Sauce Labs Mobile Testing | | AWS Device Farm | прогоны на устройствах, отчёты, видео | AWS Device Farm | | Firebase Test Lab | прогоны на устройствах Google, отчёты | Firebase Test Lab |

    Как сделать переключение local или remote через переменные окружения

    Практичный подход: в conftest.py выбирать URL и часть capabilities через переменные окружения, не меняя код под каждый CI.

    Пример концепции (упрощённо):

    Здесь важно:

  • APPIUM_URL можно подменять в CI на адрес фермы устройств
  • REMOTE_APP_ID позволяет использовать “загруженное приложение” в сервисе
  • базовые platformName и appium:automationName остаются обязательными, как и в локальном запуске
  • Матрица устройств и параллельность

    В мобильной автоматизации вы почти всегда хотите запускать не на одном устройстве.

    Матрица в GitHub Actions

    В GitHub Actions удобно использовать matrix и запускать несколько независимых джобов.

    Идея:

  • один джоб = одно устройство или одна версия ОС
  • каждая джоба сохраняет свои отчёты
  • Пример фрагмента:

    Параллельность в pytest

    Плагин: pytest-xdist

    Но для мобильного UI есть важное ограничение:

  • один процесс тестов должен владеть одним устройством или одной Appium-сессией
  • Если запустить pytest -n 4 и все воркеры пойдут в один APPIUM_URL на одно устройство, вы получите конфликты сессий и flakiness.

    Практичная стратегия:

  • параллелить на уровне CI (матрицей), выдавая каждой джобе своё устройство
  • или использовать параллельность фермы устройств (несколько одновременных сессий)
  • Секреты и безопасность: как не “утечь” токенами

    Для фермы устройств обычно нужны:

  • логин и ключ
  • токен
  • URL удалённого хаба
  • Правила:

  • не хранить секреты в репозитории
  • не печатать секреты в логах
  • хранить секреты в менеджере секретов CI
  • Ссылки:

  • GitHub Actions secrets
  • Jenkins Credentials
  • Типовые проблемы CI прогонов и как их диагностировать

    Частые причины падений “только в CI”:

  • не хватает зависимостей (SDK, драйверов Appium 2)
  • Appium не поднялся или поднялся не там (неверный URL/порт)
  • устройство не готово (эмулятор не загрузился, очередь на ферме)
  • конфликты сессий при параллельном запуске
  • не сохраняются артефакты на падении
  • Практичный минимум диагностики в CI:

  • сохранять reports/ всегда
  • сохранять лог Appium server (например, reports/appium.log)
  • прикладывать скриншоты и page source через хук pytest (как вы делали ранее)
  • Что дальше развивать после первого CI

    Когда базовый прогон в CI заработал, следующий набор улучшений обычно такой:

  • разнести smoke и regression по маркам pytest и запускать их по разным триггерам
  • добавить матрицу устройств и версий ОС
  • стандартизировать конфигурацию через переменные окружения
  • настроить стабильную публикацию Allure-отчётов
  • Так ваш фреймворк превращается из локальной демонстрации в инструмент, который реально защищает релизы и экономит время на регрессии.