Заряжаем суперсилой Appium тесты на Android +30




Привет, Хабр!

Меня зовут Николай Абалов. Я работаю в лондонском офисе Badoo в команде Mobile QA Automation. Мой коллега Раждип Варма рассказал о том, как сделать Appium-тесты быстрее и надёжнее. Ниже перевод его статьи.

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

Проблема №1: капризное приложение



Представьте, что при прогоне теста у вас в приложении возникает какое-то всплывающее сообщение (как на картинке слева). Что, если логика, управляющая отображением сообщения, неподвластна тестировщику? Как в этом случае обеспечить надёжность тестов? Или, скажем, вы хотите кликнуть по двигающемуся баннеру в вашем приложении, но тест проваливается, потому что позиция баннера изменилась к тому моменту, как Appium смог кликнуть!

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

А представьте, что ваши тесты могут сказать приложению: «Я сейчас тебя тестирую, так что отключи, пожалуйста, всплывающее сообщение с просьбой поставить приложению оценку».

Проблема №2: Appium поддерживает не всё


В нашем приложении есть фича, позволяющая, встряхнув устройство, отменить свой отказ от оценки.

Как автоматизировать с помощью Appium тряску на Android-устройстве? Есть конечная точка Shake ((POST /session/:session_id/appium/device/shake), но она не реализована для драйвера UIAutomator2.

А представьте, что ваши тесты могут сказать приложению: «Притворись, что устройство встряхнули».

Решение: позвольте приложению помочь вашим тестам, реализовав бэкдоры


Бэкдоры — способ вызова методов, определённых в коде Android-приложения, из кодовой базы автоматизации, т. е. из ваших тестов.

Давайте рассмотрим это на простом примере. Допустим, у вас есть метод foo()
в Activity, скажем, в классе LoginActivity:



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



Это решит нашу задачу «А представьте, что ваши тесты могут сказать приложению...»

Плохая новость: бэкдоры не поддерживаются в Appium «из коробки».
Хорошая новость: бэкдоры всё-таки можно реализовать в Appium!

Прежде чем переходить к решению, приведу несколько реальных примеров использования бэкдоров в автоматизации:

  • изменение URL бекенда;
  • изменение географического региона приложения;
  • выбор конкретных вариантов клиентских A/B-тестов;
  • симуляция наличия SIM-карты для приложения;
  • получение ID текущей сессии;
  • получение от приложения данных аналитики.

P. S. Делать всё это самостоятельно может быть сложно. Если вас не интересует, как я это сделал, то можете сразу переходить к главе «Быстрое начало работы с бэкдорами в вашем проекте».

Реализация бэкдоров в Appium



Архитектура «из коробки» сервера UIAutomator2

  1. APK-1 => appium-uiautomator2-server-debug-androidTest.apk
  2. APK-2 => appium-uiautomator2-server.apk

APK-1 — это пакет инструментации (instrumentation package) для APK-2. В нём есть тест — instrumentation test. Его запускает команда для драйвера «adb shell am instrument…» Единственная цель такого теста — запустить HTTP-сервер, определённый в APK-2.

Оба APK-файла выполняются в одном процессе, выделенном на иллюстрации зелёным цветом.

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

Но мы можем выжать максимум из Android-инструментации, если изменим архитектуру (как показано ниже).

Модифицированная архитектура сервера UIAutomator2:



Оснащение тестируемого приложения APK Appium

Мы влили код APK-2 в APK-1. Следовательно, инструментальный тест и HTTP-сервер теперь находятся в одном APK-файле. Назовём его Merged Server APK.

Теперь можно изменить Manifest.xml нашего нового APK-файла таким образом, чтобы его целевым инструментируемым пакетом стал пакет с названием нашего тестируемого приложения:

Это легко реализовать с помощью Ruby Gem под названием appium_instrumenter, в основе которого лежит код из calabash-android gem.

Затем подписываем Merged Server APK с помощью того хранилища ключей (keystore), что и тестируемое приложение. Теперь у нас есть кастомный инструментальный сервер, исполняющийся в том же процессе, что и тестируемое приложение (выделен зелёным цветом).

Мы инструментировали наше приложение с помощью Merged Server APK, а значит, последний может получить доступ к контексту приложения. Это означает, что мы можем попросить файл Merged Server APK вызвать методы, определённые в нашем приложении.

Создадим в Merged Server APK конечную точку, чтобы указывать, какой метод приложения нужно вызвать:

/wd/hub/session/:sessionId/backdoor.

В эту конечную точку мы можем отправить имя метода, который хотим вызвать. Merged Server APK получает контекст приложения и вызывает метод с помощью API Java Reflection.

Весь код этого объединённого с конечной точкой для бэкдоров Appium UIAutomator2 Server вы можете скачать из репозитория, из ветки ‘single_apk’.

Далее заменяем APK-файл, поставляемый с пакетом appium_uiautomator2_driver, на наш кастомный APK-файл. Всё самое сложное позади, эти манипуляции нужно проделать только один раз.

Теперь в нашем коде автоматизации тестирования есть вспомогательный метод для вызова бэкдор-методов. Вот пример на Ruby:



Вуаля! Мы получили суперсилу! Теперь можно очень просто вызвать любой публичный метод, определённый в классе Application или текущем классе Activity:



При этом поддерживается получение тестовым кодом возвращаемых значений!

Быстрое начало работы с бэкдорами в вашем проекте


1. Генерируем APK-файл Appium UIAutomator Server для вашего
приложения:



установите appium_instrumenter gem. Даже если ваши Appium-тесты написаны на Java, вам придётся один раз сделать это, поскольку я написал утилиту только на Ruby.

gem install appium_instrumenter
appium_instrumenter instrument app-debug.apk

В вашей текущей директории создастся папка ./test_servers:

test_servers
+-- appium-uiautomator2-server-debug-androidTest.apk
L-- appium-uiautomator2-server-v0.3.0.apk

Установите на устройство оба вышеупомянутых APK-файла:

adb install test_servers/appium-uiautomator2-server-debug-androidTest.apk
adb install test_servers/appium-uiautomator2-server-v0.3.0.apk

2. Теперь установите NPM-пакет appium_uiautomator2_driver из моего форка:


npm install “rajdeepv/appium-uiautomator2-driver#adb_host”

3. Определите метод backdoor():


определите метод, отправляющий данные в конечную точку для бэкдора:

http://localhost:#{APPIUM_FORWARDED_PORT}/wd/hub/session/:sessionId/backdoor

Рассмотрите приведённый выше пример реализации этого метода на Ruby. Вы можете реализовать такой же метод, если пишете на Java или любом другом языке.

4. Да прибудет с вами суперсила!


Держитесь от проблем подальше


«С большой силой приходит и большая ответственность».

Бэкдор — очень мощный инструмент. И при неправильном использовании он может привести к неприятным последствиям. Так, если мы воспользуемся бэкдором для полного изменения логики приложения, то риски тестирования будут выше, чем его ценность.

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

Вот такая история. К слову, 1 апреля Раждип выступил на CodeFest в Новосибирске. Видеозапись его доклада должна появиться летом на YouTube-канале CodeFest и на Badoo Tech.




К сожалению, не доступен сервер mySQL