🔥 Важное для QA-специалистов! 🔥
В QaRocks ты найдешь туториалы, задачи и полезные книги, которых нет в открытом доступе. Уже более 16.000 подписчиков – будь среди нас! Заходи к нам в телеграм канал QaRocks
Перевод статьи «How to Control Appium Installation with noReset and fullReset».
Мне казалось, что Appium выполняет установку приложений автоматически. Задаем путь к приложению, запускаем драйвер – все, готово.
Но потом меня осенило: я совершенно не знаю, когда мое приложение устанавливается, сбрасывает данные или сохраняет состояние с предыдущих запусков.
Эта проблема проявилась тогда, когда нам нужно было протестировать сборки релиз-кандидатов.
Путь к программе был жестко задан, поэтому для тестирования другой сборки нужно было бы менять код, коммитить и повторно его развертывать. Но наша команда проводила по 20+ тестов в день для разных сборок, и такое промедление было недопустимым.
Проблема оказалась еще глубже: я не понимал, что Appium делал с установкой. Каждый раз переустанавливал приложение? Сохранял приложение между сеансами? Очищал данные? Я опирался на первоначальные настройки, которых не понимал.
Что на самом деле делает Appium при установке
Для управления установкой приложений Appium предлагает две возможности: noReset и fullReset.
Названия непонятные, да и документация ясности не внесет.
noReset: false (по умолчанию)
- Устанавливает приложение (если оно еще не установлено на устройстве)
- Сохраняет данные приложения между сеансами драйвера
- Быстрый, но наследуется состояние с предыдущего сеанса
noReset: true
- Никогда не трогает установку приложений
- Предполагает, что приложение уже установлено
- Самый быстрый вариант. Но уйдет в ошибку, если приложение не установлено
fullReset: false (по умолчанию)
- Не удаляет приложение между сеансами
- Сохраняет данные приложения
- Быстрый, но сохраняется состояние
fullReset: true
- Полностью удаляет приложение
- Переустанавливает с нуля
- Очищает все данные; полный цикл удаления/переустановки на облачных устройствах занимает 30–60 секунд
Как взаимодействуют эти возможности
Обе опции можно сочетать нетривиальным образом. Вот, что происходит:
noReset | fullReset | Что происходит |
FALSE (по умолчанию) | FALSE (по умолчанию) | Устанавливает приложение (если оно отсутствует); сохраняет данные между сеансами |
FALSE | TRUE | Удаляет -> Переустанавливает -> Сбрасывает данные (избыточно, медленно) |
TRUE | FALSE | Предполагает, что приложение уже установлено; сохраняет данные (быстрый, но рисковый) |
TRUE | TRUE | Конфликтный: побеждает noReset, а fullReset игнорируется |
Добавляем явное поведение при установке
Работа с дефолтными настройками Appium – это здорово, но вы теряете видимость.
Когда приложение установилось? Когда оно сбросилось? Вы ждете, что Appium сделает все правильно, но сами не знаете, в чем заключается это «правильно».
Добавим следующий флаг, чтобы сделать поведение при установке явным:
private static boolean installedAppPreviously = false ;
if (!installedAppPreviously) {
capabilities.setCapability( "noReset" , false );
capabilities.setCapability( "fullReset" , false );
installedAppPreviously = true ;
}
При создании первого драйвера мы явно задаем обе опции. Тогда приложение установится (при необходимости) и никуда не удалится.
Затем, при последующих созданиях драйверов (если сеанс отрубится), мы больше не прописываем обе эти возможности. Мы опять предоставляем путь к приложению, но Appium распознает, что это приложение уже установлено, и не переустанавливает его.
Установка или управление состоянием
Возможности Appium для управления установкой определяют, что нужно сделать с приложением: установить, переустановить или оставить установленным (не удалять). Но ни одна из них не управляет состоянием приложения между тестами.
Мы обрабатываем это отдельно с перезапуском приложения в хуках @Before. Каждый тест закрывает и снова открывает приложение, очищая память и возвращаясь в исходное состояние.
Важно понять: установка и управление состоянием – это отдельные задачи. Установка делается один раз для каждого набора. Очистка состояния выполняется перед каждым тестом.
Большинство команд помещают очистку в хуки @After. Но если тест A падает, и приложение остается в нарушенном состоянии, то очистка в @After также может завершиться сбоем. Тогда тест B унаследует это несправное состояние и завершится с ложной ошибкой.
Эту проблему решает очистка в @Before. Тест B всегда начинается «с нуля», вне зависимости от того, как завершился тест A. При сбое в очистке тест В не будет 30 секунд отрабатывать логику и выдавать element not found, а завершится сразу с явной ошибкой setup failed.
Дилемма: скорость или исходное состояние
Перезапуск приложения между тестами гарантирует «чистое» (исходное) состояние, но увеличивает непроизводительные затраты. То есть, если по 5 секунд на тест… то для 50 тестов – это уже 4 минуты. На лицо дилемма: исходное состояние или скорость? Но вам важно и то, и другое.
Мы заложили непроизводительные затраты на перезапуск, а все остальное оптимизировали: перешли на стабильные нативные локаторы, очистку перевели в @Before, проработали стратегии ретраев.
В совокупности это сократило время выполнения набора с 2+ часов до ~1,5 часов. 4-минутные затраты на перезапуск стали пренебрежимо малыми.
Динамическое переключение сборок
Вторая проблема: тестирование разных сборок.
Жестко закодированный путь приложения приводит к тому, что нужно влезать в код при каждом тестировании новой сборки.
Решение: переопределять директорию приложения и имя через переменные окружения.
String appDirectory = System.getenv("APP_DIRECTORY");
String appName = System.getenv("APP_NAME");
if (appName != null && !appName.isEmpty()) {
if (appDirectory == null || appDirectory.isEmpty()) {
throw new IllegalArgumentException("APP_NAME requires APP_DIRECTORY");
}
android_app = appDirectory + "/" + appName + ".apk";
iOS_app = appDirectory + "/" + appName + ".ipa";
} else {
android_app = "app-develop.apk";
iOS_app = "app-develop.ipa";
}
Теперь можно протестировать любую сборку, не меняя код:
# Тестируем дефолтную сборку mvn test # Тестируем релиз-кандидата 1.29.0 APP_DIRECTORY="builds/rc" APP_NAME="1.29.0" mvn test # Тестируем функциональную ветку APP_DIRECTORY="builds/feature" APP_NAME="new-login" mvn test
Переменная окружения переопределяет значения по умолчанию.
А ваш CI-инструмент умеет передавать эти переменные из UI, так что QA сможет протестировать любую сборку без знания Java.
Настоящая победа
Предсказуемость.
Раньше я не мог точно сказать, когда приложение устанавливалось, и в каком состоянии пребывало.
Сейчас:
- Приложение устанавливается однократно при запуске тестового набора (
noReset: false,fullReset: false) - При последующих пересозданиях драйвера шаг с установкой пропускается (флаг
installedAppPreviously) - Приложение перезапускается между тестами – возврат к исходному состоянию (отдельно от установки)
- Выбор сборки происходит через переменные окружения (без изменений кода)
Если тест сбоит, я точно знаю, в каком состоянии он запускался. Я могу последовательно воспроизвести сбой и протестировать различные сборки без повторного развертывания кода.
Возможности для установки в Appium не совсем понятны. Стандартные настройки отработают в простых случаях, но сломаются, если вы захотите управлять процессом. Явное поведение (через комбинацию noReset, fullReset и флаги) превращает танцы с бубном во вполне понятную, предсказуемую и отлаживаемую логику.
