среда, 16 мая 2018 г.

Appium: автоматизация Android приложений

Appium - известный инструмент автоматизации мобильных приложений (Android & iOS), с возможностью написания тестов на большом количестве языков программирования / фреймворках, среди которых есть Python (полный список поддерживаемых языков можно найти на сайте).

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

Дополнительные условия:
1) Пункты меню могут быть вложенными
2) Меню динамическое, меняется каждый день вручную несколькими людьми
3) Надо проверить каждую категорию (кликнуть на каждый пункт меню и проверить сколько будет доступно товаров на странице)



Установка:
Устанавливается Appium достаточно просто, хотя и не в один клик:
1) Устанавливаем Android SDK (life hack: установите в пару кликов Android Studio, и SDK установится автоматически)
2) Устанавливаем Appium (на сайте есть подробная пошаговая инструкция)
3) Устанавливаем питон клиент
4) Подключаем реальный Android телефон к компьютеру через USB, включаем USB debugging.
5) Пишем первый простой скрипт и запускаем его (скрипт просто запустит мобильное приложение).

Если приложение запустилось - считаем, что установка прошла успешно.

В поиске идентификаторов элементов нам будет помогать инструмент uiautomatorviewer, который ставится вместе с Android SDK. Если у вас MacOS, то запустить его можно так:


/Users/<user>/Library/Android/sdk/tools/bin/uiautomatorviewer

После запуска нажимаем на кнопку Device Snapshot -> , ждем и дальше просто наводим мышку на нужный нам элемент, получаем все его свойства.

Способов идентификации элементов Appium предоставляет много, но в моем случае была только возможность идентифицировать элементы либо по ClassName, либо по XPath.

Пример того, как выглядит код работы с элементами:

driver.find_elements_by_id('com.alibaba.aliexpresshd:id/title')[8].click()

Можно скролить экран (для Python есть два метода - swipe и TouchAction - оба метода требуют ручного подбора значений для скролинга и скорости скролинга, так как не все события распознаются как скролл). Пример скрола вниз:

driver.swipe(70, 510, 70, 100, 400)

Сложности при написании автотестов и скриптов возникают там, где мы сталкиваемся с особенностями Android и мобильных устройств. Даже если вы глазами уже видите нужную Activity / page или элементы, это совсем не означает что Appium тоже видит все эти элементы и готов на них нажимать или читать их атрибуты. 

Простой пример кода (в котором мы по XPath ищем дочерние элементы текущего элемента):

menu_items = menu.find_elements_by_xpath(
    "//*[string-length( @text ) > 1 and not(starts-with(@text, ' '))]")
print( len(menu_items) )
menu_items = menu.find_elements_by_xpath(
    "//*[string-length( @text ) > 1 and not(starts-with(@text, ' '))]")
print( len(menu_items) )

Такой код иногда печатает два одинаковых числа (что от него и ожидается), но иногда печатает два разных числа (например, 7 и 8 - то есть со второго раза поиск по XPath нашел на один элемент больше). 

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

Хотя есть и отличия от Selenium - доступны не все фичи, чаще всего используется другая логика локаторов, есть отличия в написании XPath (например @text вместо text() ).
Главное отличие от Selenium - если вы успешно обнаружили элемент, это еще не означает, что вы сможете получить значения его атрибутов.

Например:
1) Ищем элементы по XPath
2) В цикле проходимся по списку найденных элементов и запрашиваем element.text
3) Получаем  (иногда) исключение на втором шаге "element couldn't be found by this locator" - неожиданно, ведь казалось что элемент уже найден и он даже есть в виде объекта, но его свойства читать нельзя :) - видимо, есть какая-то ленивая инициализация внутри.

Итого:

Положительные особенности Appium:
1) Сравнительно легко установить, настроить, запустить первый тест.
2) Поддерживается Android & iOS.
3) Много способов идентификации элементов.
4) Синтаксис очень похож на Selenium, благодаря чему можно очень быстро начать писать тесты, не надо изучать новый API.
5) Позволяет писать DeepTests (то есть тесты, которые парсят структуру текущей Activity и сами определяют элементы, с которыми надо работать и меняют список проверок в зависимости от того что сейчас представлено на экране - а не просто действуют по строго определенным шагам)

Отрицательные особенности Appium:
1) Не всегда находит элементы с первого раза, и даже если уже нашел элемент - не всегда это означает что мы можем читать свойства найденных элементов. В итоге приходится писать функции-обертки и делать таймауты, чтобы Appium стабильно находил все элементы и мог их нажимать и забирать их свойства.
2) Нет возможности распознавать элементы по картинке (или я пока не понял как) - то есть для автоматизации мобильных игр Appium будет сложноват.
3) Идентифицировать элементы достаточно непросто, хотя Appium поддерживает несколько видов локаторов, но иногда приходится писать длинные строки и циклы, чтобы отобрать нужные элементы
4) При написании тестов придется столкнуться и узнать много маленьких секретов и особенностей того как устроены интерфейсы мобильных приложений и какие баги есть в самом Appium. Не смотря на низкий порог входа (для запуска первого теста) - порог для написания боевых тестов намного выше (придется сталкиваться с неочевидными вещами, гуглить и брутфорсить и тратить время на то чтобы сделать простые вещи).


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