В первой части статьи мы рассказали, как легко и быстро построить инфраструктуру для запуска UI-тестов на Android с помощью Appium и Selenoid. Продолжаем историю и рассказываем, как внедрили в схему запуск UI-тестов на iOS.
![](https://habrastorage.org/getpro/habr/upload_files/d72/2f8/260/d722f8260ec9dd080f9674e28c8c51aa.png)
Масштабируемся с GGR
Максимальное количество параллельных потоков в пределах хоста ограничено его ресурсами. Поэтому нам понадобился инструмент для объединения нескольких хостов в один кластер. Для этого мы используем Go Grid Router (GGR) от ребят из Aerokube. Согласно описанию из документации, GGR — балансировщик нагрузки, используемый для создания масштабируемых и высокодоступных кластеров Selenium.
![](https://habrastorage.org/getpro/habr/upload_files/2fa/7d2/ebc/2fa7d2ebc6f16e6c8de72a63b4e1f317.gif)
В схеме проект с тестами обращается к GGR. Он опрашивает указанные в его конфигурации Selenoid и распределяет нагрузку между ними в зависимости от платформы, наличия свободных потоков и установленного удельного веса каждого Selenoid.
Развернуть GGR и GGR UI несложно:
-
Устанавливаем Docker.
-
Создаём каталог для файлов конфигурации GGR
mkdir -p /etc/grid-router/quota
. -
Создаём файл users.htpasswd
$ htpasswd -bc /etc/grid-router/users.htpasswd test test-password
. -
Создаём файл с квотами, где в качестве хоста указываем адрес развёрнутого Selenoid:
$ cat /etc/grid-router/quota/test.xml <qa:browsers xmlns:qa="urn:config.gridrouter.qatools.ru"> <browser name="android" defaultVersion="10.0" defaultPlatform="android"> <version number="10.0"> <region name="1"> <host name="0.0.0.0" port="4444" count="1"/> </region> </version> </browser> </qa:browsers>
-
Запускаем контейнер с GGR:
docker run -d \ --name ggr \ -v /etc/grid-router/:/etc/grid-router:ro \ --net host aerokube/ggr:latest-release \ -listen=:4445 -guests-allowed
-
В проекте тестов меняем порт Appium на порт запущенного GGR:
val driver = AndroidDriver(URL("http://localhost:4445/wd/hub"), capabilities)
-
Запускаем контейнер с GGR UI:
docker run -d \ --name ggr_ui \ -p 8888:8888 \ -v /etc/grid-router/quota:/etc/grid-router/quota:ro \ aerokube/ggr-ui:latest-release
-
Запускаем контейнер с Selenoid UI, где в
selenoid-uri
передаём порт GGR UI:
docker run -d \ --name selenoid-ui \ -p 4446:4446 \ --link selenoid:selenoid \ aerokube/selenoid-ui:1.10.4 \ --selenoid-uri "<http://ggr-ui:8888>"
Теперь наш Selenoid UI должен отображать состояние всех Selenoid-кластеров, подключенных к GGR.
Приступаем к запуску тестов на iOS
Для запуска UI-тестов на iOS мы используем собственную ферму Mac mini. Таким же образом ферму можно собрать из списанных рабочих макбуков или взять их в аренду. На каждый хост необходимо установить:
-
Appium (для работы с iOS≥16 необходимо использовать Appium 2.x).
-
Xcode.
Первые проблемы
Мы не могли повторить структуру, используемую при запуске Android-тестов, так как не нашли способ запускать iOS-симуляторы в Docker-контейнерах. Рассматривали вариант запуска Docker-OSX, но возникли сомнения по легальности его использования для целей, не связанных с OS X Security Research. Мы решили пойти другим путём.
![](https://habrastorage.org/getpro/habr/upload_files/109/b97/4c6/109b974c6dfd45da58203479e470c2b9.gif)
Итерация 1: GGR→Appium
Добавили Appium (порт 4723) в качестве хоста Selenoid для iOS-тестов в ранее созданный конфиг GGR:
<qa:browsers xmlns:qa="urn:config.gridrouter.qatools.ru"> <browser name="android" defaultVersion="10.0" defaultPlatform="android"> <version number="10.0"> <region name="1"> <host name="0.0.0.0" port="4444" count="1"/> </region> </version> </browser> <browser name="iPhone 14" defaultVersion="16.2" defaultPlatform="iOS"> <version number="16.2"> <region name="1"> <host name="0.0.0.0" port="4723" count="1"/> </region> </version> </browser> </qa:browsers>
Схема iOS-части в таком случае выглядит следующим образом:
![](https://habrastorage.org/getpro/habr/upload_files/962/486/0ef/9624860efeb58a95cd993a4acfc750c4.png)
Используемая в итерации структура работоспособна. Проблема в том, что в данном случае мы можем запускать тесты только в одном потоке на каждом Mac mini, что расточительно. Также кластер не будет отображаться в Selenoid UI.
Итерация 2: GGR→Selenoid→Appium
Selenoid позволяет работать не только с контейнерами. В связи с вышеуказанными проблемами мы решили использовать Selenoid и с iOS-тестами, но в виде исполняемого файла:
-
Создаём файл конфигурации browsers.json.
-
В конфигурации указываем Appium и параметры запуска:
{ "iPhone 14": { "default": "16.2", "versions": { "16.2": { "image": ["appium", "--log-timestamp", "--log-no-colors", ...] } } } }
-
Предоставим разрешение на исполнение файла Selenoid. В нашем случае мы использовали
chmod 755
. -
Запускаем Selenoid через терминал. Мы использовали следующие параметры:
selenoid -conf ~/browsers.json -disable-docker -capture-driver-logs -service-startup-timeout 4m -session-attempt-timeout 4m -timeout 6m -limit 2
.-
Указанные таймауты необходимы, так как стандартных может быть недостаточно для скачивания приложения из облачного хранилища и запуска симулятора.
-
Параметром
-limit
установили максимальное количество запускаемых симуляторов. На это значение в дальнейшем будет опираться GGR. При выставлении параметра опираемся на производительность хоста. -
Подробнее о параметрах запуска можно почитать в документации Selenoid.
-
-
При необходимости на каждом Mac mini можно создать PLIST-файл для автозагрузки Selenoid на случай внезапного перезапуска системы.
Схема работы кластера теперь выглядит так:
![](https://habrastorage.org/getpro/habr/upload_files/9a1/866/cbe/9a1866cbe7260531a0ea04b03962190e.png)
При таком подходе мы частично получаем функциональность Selenoid UI и возможность запускать тесты в несколько потоков на одном хосте.
![](https://habrastorage.org/getpro/habr/upload_files/080/a63/141/080a631415100df92e1893cc0e2183fe.png)
Недостатком является то, что на каждом Mac mini надо вручную проделывать много рутинных манипуляций по созданию симулятора и привязыванию его к Appium через указание UUID и назначение портов. Это может стать проблемой, когда в дальнейшем появится необходимость перехода на новую версию iOS.
Итерация 3: GGR→Selenoid→Bash→Appium
У нас крупная ферма Mac mini, которая будет продолжать расти. Поэтому мы искали способ облегчить масштабирование, чтобы не создавать руками симуляторы и прибивать их к Appium. В прошлой схеме у Appium и симуляторов было бы длительное время жизни, что могло повлечь непредсказуемые последствия.
В процессе поиска решения мы обнаружили, что в файле конфигурации Selenoid в качестве хоста можно указать bash-скрипт:
{ "iPhone 14": { "default": "16.2", "versions": { "16.2": { "image": ["~/bin/config/start_appium.sh", "iPhone 14"] } } } }
Так он выглядит у нас:
#!/bin/bash set -ex DEVICE_NAME=$1 APPIUM_PORT=$(echo $2 | cut -d '=' -f 2) function clean() { if [ -n "$APPIUM_PID" ]; then kill -TERM "$APPIUM_PID" fi if [ -n "$DEVICE_UDID" ]; then xcrun simctl delete $DEVICE_UDID fi } trap clean SIGINT SIGTERM # Каждый симулятор имеет udid, поэтому чтобы запустить одинаковые устройства параллельно - клонируем и запускаем # только клоны. Нельзя клонировать запущенное устройство. После закрытия сессии удаляем клон. cloned_device_name="[APPIUM] ${DEVICE_NAME} ($(date +%Y%m%d%H%M%S))" DEVICE_UDID=$(xcrun simctl clone "$DEVICE_NAME" "$cloned_device_name") # https://github.com/appium/appium-xcuitest-driver#important-simulator-capabilities WDA_LOCAL_PORT=$(($APPIUM_PORT+1000)) MJPEG_SERVER_PORT=$(($WDA_LOCAL_PORT+1000)) DEFAULT_CAPABILITIES='"appium:udid":"'$DEVICE_UDID'","appium:automationName":"'XCUITest'","appium:wdaLocalPort":"'$WDA_LOCAL_PORT'","appium:mjpegServerPort":"'$MJPEG_SERVER_PORT'"' appium --base-path=/wd/hub --port=$APPIUM_PORT --log-timestamp --log-no-colors --allow-insecure=get_server_logs,adb_shell \ --allow-cors --log-timestamp --log {choose_directory_for_logs} \ --default-capabilities "{$DEFAULT_CAPABILITIES}" & APPIUM_PID=$! wait
В случае использования скрипта обратите внимание на объявленные capabilities и параметры запуска Appium. Здесь они указаны с учётом запуска Appium 2.x — для Appium 1.x не требуется указание вендора в capabilities и нет возможности указать —base-pat.
Скрипт решает проблемы параллельного запуска симуляторов:
-
Когда мы напрямую подключаем к GGR несколько аппиумов с одного Mac mini, возникает проблема с эмуляторами — нельзя запустить один и тот же эмулятор с одинаковыми UDID. Надо каждый раз вручную дублировать и зашивать/хардкодить UDID. Например, в случае необходимости изменения версии iOS или модели симулятора.
-
Плохая масштабируемость. Необходимо каждый раз запускать Appium руками и регулярно проверять его на отсутствие конфликтов с портами и симуляторами.
Использование Selenoid позволяет упростить это до одного скрипта, который не создаёт конфликтов между несколькими парами Appium + Simulator в пределах одного хоста. Он запускает Appium и убивает его при получении соответствующего сигнала от Selenoid, а также динамически клонирует симуляторы при запуске и удаляет их после завершения сессии.
Получили такую схему:
![](https://habrastorage.org/getpro/habr/upload_files/c93/078/ee8/c93078ee8ea2fbdcb83fc27419b21c3f.png)
Далее добавляем адреса Selenoid каждого Mac mini в файл конфигурации развёрнутого GGR, объединяя Android- и iOS-структуры:
![](https://habrastorage.org/getpro/habr/upload_files/31c/d42/f06/31cd42f061553ef368c81d923514a244.png)
Итог
Собранная инфраструктура позволяет прогонять на 36 потоках около 500 UI-тестов в час суммарно на обеих платформах. Добавление нового хоста для Android-тестов полностью автоматизировано с помощью воркфлоу на GitHub Actions и занимает около двух минут. В ближайших планах автоматизировать развёртывание Selenoid-кластера и на Mac mini.
![](https://habrastorage.org/getpro/habr/upload_files/bc5/54b/317/bc554b317b83c114563b9d6fddc3af69.gif)
В дальнейшем хочется попробовать запускать контейнеры Docker-OSX на Mac mini с Linux, чтобы унифицировать все процессы и облегчить развертывание на них, не нарушая при этом правила использования macOS. Возможно, у вас есть такой опыт — будем рады, если вы поделитесь им в комментариях.
ссылка на оригинал статьи https://habr.com/ru/articles/732226/
Добавить комментарий