Автор: Безпалько Павел 
Немного про SelenoidЕсли вы читаете эту статью, то наверняка слышали о Selenoid. Это мощное решение написанное на Go от компании aerokube для управления Docker-контейнерами, в которых «завёрнуты» все необходимые программы и драйверы для тестирования в браузерах или на Android-эмуляторах. Как утверждают производители Selenoid, их решение потребляет намного меньше ресурсов, чем, например тот же Selenium. В то время, как первый имеет удобный веб интерфейс (в документации к Selenium нет никаких упоминаний про UI) и также возможность параллельного запуска тестов. У Selenoid также есть отличная поддержка, большое коммьюнити и достаточно хорошая документация.
Отмечу, что Selenoid работает только на Linux. За исключением, когда вам нужно запустить Selenoid без Docker. Но это уже совсем другая история. В статье на своем опыте я расскажу и покажу с какими сложностями сталкивался и как их решал. Хотелось бы подчеркнуть, что возможно у вас возникнут подобные проблемы при установке и настройке данного инструмента. Установка и настройка SelenoidЧтобы начать установку и настройку Selenoid, нужно сперва установить Docker. Для этого идём на официальный сайт, выбираем свою ОС и устанавливаем программу. Чтобы удобно было управлять Selenoid, сразу устанавливаем docker-compose. Это решение, которое позволяет описать настройки для нескольких Docker контейнеров в одном yaml файле и упралять ими. Для этого нужно перейти на сайт и скачать в директорию /usr/local/bin последний доступный релиз. cd /usr/local/bin/
sudo wget https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-linux-x86_64
Переименовываем файл. sudo mv docker-compose-linux-x86_64 docker-compose
И даём права на запуск. sudo chmod +x docker-compose
Теперь нам нужно создать docker-compose.yml файл для docker-compose. У нас он получился вот таким. version: "3"
services:
selenoid:
image: aerokube/selenoid:latest-release
container_name: selenoid
network_mode: bridge
ports:
- "4444:4444"
environment:
- "OVERRIDE_VIDEO_OUTPUT_DIR=$PWD/video"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/etc/selenoid:/etc/selenoid"
- "./video:/opt/selenoid/video"
- "/opt/selenoid/logs:/opt/selenoid/logs"
command: ["-conf", "/etc/selenoid/browsers.json", "-limit", "20", "-retry-count", "1000", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs", "-max-timeout", "20m0s", "-session-attempt-timeout", "15m", "-timeout", "10m", "-service-startup-timeout", "10m"]
selenoid-ui:
image: aerokube/selenoid-ui:latest-release
container_name: selenoid-ui
network_mode: bridge
ports:
- "8080:8080"
links:
- selenoid
command: -selenoid-uri 'http://selenoid:4444'
depends_on:
- selenoid
Здесь важно понимать, что без yaml файла docker-compose работать не будет. Далее нам нужно создать и настроить конфигурационный файл /etc/selenoid/browsers.json. {
"android": {
"default": "10.0",
"versions": {
"10.0": {
"image": "selenoid/android:10.0",
"port": "4444",
"path": "/wd/hub",
"env": [
"VERBOSE=true",
"APPIUM_ARGS=--log-level debug"
],
"selenoid:options": {
"enableVNC": "true",
"enableLog": "true",
"enableVideo": "true"
}
}
}
}
}
После всей подготовки запускаем Selenoid. Для этого переходим в директорию, где у вас лежит подготовленный ранее файл docker-compose.yml и выполняем команду docker-compose up -d
После этого скачаются образы selenoid и selenoid-ui и запустятся контейнеры. 
Проверяем, что selenoid и его ui запущен docker-compose ps -a

Up <time> говорит о том, что контейнеры запущены и работают Теперь идем в браузер и открываем ui selenoid, чтобы точно убедиться, что все заработало. http://<hostname>:8080. 
Две зеленые надписи CONNECTED говорят о том, что все хорошо. Но тесты запускать пока рано. Переходим на сайт с образами для Android и скачиваем образ с Android 10. docker pull selenoid/android:10.0
Запуск тестов и пара проблемМы установили Selenoid, скачали образ с Android 10, теперь можно приступать к тестированию. Мы используем стек: Gradle. Java. JUnit5. Appium.
С разбегу запустить тесты на Selenoid не получилось. При первом запуске получили вот такую ошибку. 
Этот лог нам ничего не сказал. Лезем в логи контейнера с Android-эмулятором. 
Здесь уже поинтересней. Оказалось, что на ПК выключена виртуализация. Заходим в BIOS и включаем виртуализацию. У каждого производителя виртуализация включается по разному.  
Далее столкнулись с другой проблемой. При запуске тестов с таким capability... final DesiredCapabilities device = new DesiredCapabilities();
device.setCapability("deviceName", "android");
device.setCapability("version", "10.0");
device.setCapability("appPackage", "ru.app.mobile.android");
device.setCapability("appActivity", "ru.app.mobile.android.splash.presentation.activity.SplashActivity");
device.setCapability("enableVNC", true);
device.setCapability("enableLog", true);
device.setCapability("enableVideo", true); // Uncomment this to record video
driver = new AppiumDriver<>(new URL(
"http://<selenoid>:4444/wd/hub" //Replace with correct host and port
), device);
...получали такую ошибку. .
java
org.openqa.selenium.SessionNotCreatedException: Unable to create a new remote session. Please check the server log for more details. Original error: All non-standard capabilities should have a vendor prefix. The following capabilities did not have one: enableLog,enableVNC,enableVideo,version
Build info: version: '3.141.59', revision: 'e82be7d358', time: '2018-11-14T08:17:03'
System info: host: 'MacBook-Pro-Pavel.local', ip: 'fe80:0:0:0:14d7:d36:4180:ab86%en0', os.name: 'Mac OS X', os.arch: 'aarch64', os.version: '13.5.1', java.version: '11.0.20'
Driver info: driver.version: AppiumDriver
remote stacktrace: InvalidArgumentError: All non-standard capabilities should have a vendor prefix. The following capabilities did not have one: enableLog,enableVNC,enableVideo,version
После общения в чате поддержки, оказалось, что capability должен выглядеть примерно вот так. DesiredCapabilities device = new DesiredCapabilities();
device.setCapability("appium:deviceName", "android");
device.setCapability("appium:version", "10.0");
device.setCapability("appium:app", "ru.app.mobile.android");
device.setCapability("appium:appPackage", "ru.app.mobile.android.feature");
device.setCapability("appium:sessionTimeout", "15m");
device.setCapability("appium:enableVNC", true);
device.setCapability("appium:enableLog", true);
device.setCapability("appium:enableVideo", true); // Uncomment this to record video
device.setCapability(NEW_COMMAND_TIMEOUT, "3000");
AppiumDriver<RemoteWebElement> driver = new AndroidDriver<>(new URL(
"http://<selenoid>:4444/wd/hub" //Replace with correct host and port
), device);
Но при таком capability в selenoid-ui были видны только логи appium, без VNC. 
Оказалось, что enableVNC нужно запускать по другому. Map<String, Object> selenoidOptions = Map.of("enableVNC", true,
"enableLog", true,
"enableVideo", true);
DesiredCapabilities device = new DesiredCapabilities();
device.setCapability("appium:deviceName", BrowserType.ANDROID);
device.setCapability("appium:platform", Platform.ANDROID);
device.setCapability("appium:version", "10.0");
device.setCapability("appium:app", "ru.app.mobile.android.feature");
device.setCapability("appium:appPackage", "ru.alfabank.mobile.android.feature");
device.setCapability("appium:appActivity", "ru.alfabank.mobile.android.splash.presentation.activity.SplashActivity");
device.setCapability("appium:sessionTimeout", "15m");
device.setCapability("selenoid:options", selenoidOptions);
device.setCapability(NEW_COMMAND_TIMEOUT, "3000");
AppiumDriver<RemoteWebElement> driver = new AndroidDriver<>(new URL(
"http://<selenoid>:4444/wd/hub" //Replace with correct host and port
), device);
driver.manage().timeouts().implicitlyWait(launchPropertyProvider.getImplicitWaitDuration(), TimeUnit.SECONDS);
return driver;

Вот теперь можно запускать тесты или приступать к сборке собственного образа с Android эмулятором. Сборка собственного образа с Android эмуляторомКлонируем репозиторий. git clone https://github.com/aerokube/images
Переходим в директорию selenium и запускаем скрипт automate_android.sh. cd selenium
./automate_android.sh
Вводим последнею стабильную версию appium, либо оставляем версию по умолчанию. Specify Appium version: [2.0.1] 2.1.3
Далее выбираем какой тип Android будем использовать: выбираем с Google сервисами (google_apis_playstore) или без (google_apis). Specify Android image type (possible values: "default", "google_apis", "google_apis_playstore", "android-tv", "android-wear"): [default] google_apis_playstore
Следующим пунктом выбираем, какой интерфейс приложения будет использоваться. Specify Application Binary Interface (possible values: "armeabi-v7a", "arm64-v8a", "x86", "x86_64"): [x86] x86_64
Далее выбираем версию Android — доступны версии с 4 по 14. В примере ниже выбрал версию 12.0. Specify Android version: [8.1] 12.0
Идем в Android Studio, смотрим список доступных эмуляторов... 
...и вводим нужное название эмулятора... Specify device preset name if needed (e.g. "Nexus 4"): Pixel 6a
...или оставляем значение по умолчанию. Далее выбираем объем SD карты для эмулятора, оставляем по умолчанию 500 Mb или указываем свое значение. Specify SD card size, Mb: [500]
Следующим шагом указываем объем памяти для эмулятора, оставляем значение по умолчанию или указываем свое значение. Specify userdata.img size, Mb: [500]
Следующим шагом указываем нужно ли устанавливать Chrome для web тестирования на Android, оставляем значение по умолчанию. Are you building a Chrome Mobile image (for mobile web testing): [n]
Указываем версию Chromedriver. Оставляем значение по умолчанию. Specify Chromedriver version if needed (required for Chrome Mobile):
Как будет называться наш docker образ? Оставляем по умолчанию или задаем свое имя. Specify image tag: [selenoid/android:12.0]
И, наконец, нас спрашивают «Добавить ли снимок быстрой загрузки». Оставляем значение по умолчанию. Add Android quick boot snapshot? [y]
После начнется сборка образа с заданными параметрами. 
Когда сборка закончится, проверяем, наш новый образ. 
Снимок экрана 2023-09-26 в 12.51.32
Теперь нам нужно добавить образ в конфигурацию selenoid. Для этого открываем и правим файл /etc/selenoid/browsers.json. {
"android": {
"default": "12.0",
"versions": {
"10.0": {
"image": "selenoid/android:10.0",
"port": "4444",
"path": "/wd/hub",
"env": [
"VERBOSE=true",
"APPIUM_ARGS=--log-level debug"
],
"selenoid:options": {
"enableVNC": "true",
"enableLog": "true",
"enableVideo": "true"
}
},
"12.0": {
"image": "selenoid/android:12.0",
"port": "4444",
"path": "/wd/hub",
"env": [
"VERBOSE=true",
"APPIUM_ARGS=--log-level debug"
],
"selenoid:options": {
"enableVNC": "true",
"enableLog": "true",
"enableVideo": "true"
}
}
}
}
}
Перезапускам Selenoid, чтобы изменения подхватились. docker-compose down && docker-compose up -d
ИтогИзначально я планировал разместить этот материал только для внутреннего использования в Confluence, чтобы делиться материалом по теме. Но по мере написания статьи, все больше понимал, что мой опыт может помочь не только мне и моим коллегам, но и остальным. Поэтому я искренне надеюсь, что здесь вы найдете решение основных проблем, с которыми я столкнулся. А сэкономленное время вы сможете потратить на погружение в прекрасный мир тестирования или другие приятные вещи. Желаю, чтобы у вас все получилось! Selenoid хороший инструмент. Например, нам он дал нам следующее: возможность сразу запускать тесты, без необходимости настраивать тестовую среду локально; паралелльный запуск тестов, без необходимости дополнительной настройки тестовой среды; тестирование на всех версиях Android и всех доступных эмуляторах; удобный web интерфейс и мониторинг процесса тестирования; запись видео тестирования.
Также в планах есть интеграция Appium Inspector с Selenoid, чтобы коллегам было еще удобнее писать тесты, без необходимости поднимать и настраивать локально тестовую среду. Обсудить в форуме |