Проблема: UI-тесты, запускаемые с помощью Appium, падают.
Задача: найти причину, и пофиксить.
Что бы воспроизвести проблему — установили Android Studio на рабочую машинку и создали устройство. Теперь — можно настроить «тестовый тест», а потом попробовать запустить наши реальные тесты.
Забегая наперёд — проблема была найдена, но решения не нашёл, потому пришлось костылить в Jenkins-джобе.
Сама проблема на билд-агенте выглядит так:
[ERROR] setUp(workoutme.subscriptions.SubscriptionTests) Time elapsed: 60.857 s <<< FAILURE!
org.openqa.selenium.WebDriverException:
It is impossible to create a new session because ‘createSession’ which takes HttpClient, InputStream and long was not found or it is not accessible
Build info: version: ‘3.13.0’, revision: ‘2f0d292’, time: ‘2018-06-25T15:24:21.231Z’
System info: host: ‘project-ci’, ip: ‘127.0.1.1’, os.name: ‘Linux’, os.arch: ‘amd64’, os.version: ‘4.15.0-39-generic’, java.version: ‘1.8.0_181’
Driver info: driver.version: AndroidDriver
Caused by: java.lang.reflect.InvocationTargetException
Caused by: org.openqa.selenium.WebDriverException:
An unknown server-side error occurred while processing the command. Original error: Error getting AVD with retry. Original error: Condition unmet after 60159 ms. Timing out.
Build info: version: ‘3.13.0’, revision: ‘2f0d292’, time: ‘2018-06-25T15:24:21.231Z’
System info: host: ‘project-ci’, ip: ‘127.0.1.1’, os.name: ‘Linux’, os.arch: ‘amd64’, os.version: ‘4.15.0-39-generic’, java.version: ‘1.8.0_181’
Driver info: driver.version: AndroidDriver
remote stacktrace: UnknownError: An unknown server-side error occurred while processing the command. Original error: Error getting AVD with retry. Original error: Condition unmet after 60159 ms. Timing out.
Для запуска тестов на рабочей машине нам потребуется Selenium в роли сервера, к которому будет подключаться Appium для получения запросов, которые он потом будет передавать на приложения и возвращать ответы в Selenuim.
К Appium будут подключаться ноды Selenium-а (в данном случае — AVD-устройства), на которых Appium будет выполнять тесты.
Содержание
Запуск тестов локально
Запуск Selenuim
Загружаем файл со страницы https://www.seleniumhq.org/download/.
Документация по запуску Selenium-хаба — тут>>>.
Запускаем его:
[simterm]
$ java -jar selenium-server-standalone-3.141.59.jar -role hub 12:43:48.190 INFO [GridLauncherV3.parse] - Selenium server version: 3.141.59, revision: e82be7d358 12:43:48.273 INFO [GridLauncherV3.lambda$buildLaunchers$5] - Launching Selenium Grid hub on port 4444 2018-11-21 12:43:48.739:INFO::main: Logging initialized @892ms to org.seleniumhq.jetty9.util.log.StdErrLog 12:43:48.890 INFO [Hub.start] - Selenium Grid hub is up and running 12:43:48.890 INFO [Hub.start] - Nodes should register to http://172.18.0.1:4444/grid/register/ 12:43:48.890 INFO [Hub.start] - Clients should connect to http://172.18.0.1:4444/wd/hub
[/simterm]
Запуск Appium
Устанавливаем его:
[simterm]
$ npm install appium
[/simterm]
Проверяем:
[simterm]
$ ~/node_modules/.bin/appium --help usage: /home/setevoy/node_modules/.bin/appium [-h] [-v] [--shell] [--reboot] [--ipa IPA] [-a ADDRESS] [-p PORT] [-ca CALLBACKADDRESS] [-cp CALLBACKPORT] ...
[/simterm]
Создаём файл настроек для ноды, пример есть тут>>>:
{ "capabilities": [ { "deviceName": "Android Emulator", "version":"8.0", "platform":"Android" } ], "configuration": { "cleanUpCycle":3000, "timeout":30000, "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy", "url":"http://:172.18.0.1:4625/wd/hub", "host": "172.18.0.1", "port": 4625, "maxSession": 2, "register": true, "registerCycle": 5000, "hubPort": 4444, "hubHost": "172.18.0.1" } }
Запускаем Appium:
[simterm]
$ ~/node_modules/.bin/appium -p 4625 --nodeconfig /home/setevoy/Temp/nexus_5_node.json [Appium] Welcome to Appium v1.9.1 [Appium] Non-default server args: [Appium] port: 4625 [Appium] nodeconfig: /home/setevoy/Temp/nexus_5_node.json [debug] [Appium] Starting auto register thread for grid. Will try to register every 5000 ms. [Appium] Appium REST http interface listener started on 0.0.0.0:4625 [debug] [Appium] Appium successfully registered with the grid on http://172.18.0.1:4444 [HTTP] --> GET /wd/hub/status [HTTP] {} [debug] [GENERIC] Calling AppiumDriver.getStatus() with args: [] [debug] [GENERIC] Responding to client with driver.getStatus() result: {"build":{"version":"1.9.1","git-sha":"f9ee7d068e14f10729f88ebd85db802c2e8ac21a","built":"2018-09-20T18:14:29Z"}} [HTTP] <-- GET /wd/hub/status 200 5 ...
[/simterm]
Проверяем лог Selenuim:
[simterm]
... 12:57:58.176 WARN [BaseRemoteProxy.<init>] - Max instance not specified. Using default = 1 instance 12:57:58.182 INFO [DefaultGridRegistry.add] - Registered a node http://172.18.0.1:4625 ...
[/simterm]
Нода подключилась — хорошо, всё работает, можно тестить.
Тестирование с Appium
Пробуем запустить наши тесты, что бы воспроизвести ошибку.
Вообще задача — воспроизвести ошибку:
[INFO] Running TestSuite
…
An unknown server-side error occurred while processing the command. Original error: Error getting AVD with retry. Original error: Condition unmet after 60152 ms. Timing out.
…
Устройство и подключение к нему описаны в pom.xml
тестов:
... <profile> <id>linux-emulator-8.0</id> <properties> <hub.url>http://0.0.0.0:4723/wd/hub</hub.url> <device.name>Android Emulator</device.name> <avd>Nexus_5X_API_26</avd> <platform.name>Android</platform.name> <platform.version>8.0</platform.version> <auto.grant.permissions>true</auto.grant.permissions> <full.reset>true</full.reset> <no.reset>false</no.reset> <app.path>/home/project/project/project.apk</app.path> <app.name>com.gen.workoutme</app.name> <screenshot.path> ${project.basedir}/../screenShots/${platform.name}/${platform.version}/${device.name}/ </screenshot.path> <automation.name>UiAutomator2</automation.name> </properties> </profile> ...
Задаём $ANDROID_HOME
, и добавляем tools
и platform-tools
в $PATH
:
... export ANDROID_HOME=/home/setevoy/Android/Sdk export PATH=$PATH:$GOPATH/bin:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools ...
Запускаем Appium на порту 4723, как указано в настройках ноды, в файле pom.xml
:
[simterm]
$ ~/node_modules/.bin/appium -p 4723 > /home/username/appium.log
[/simterm]
Для дебага — перенаправляем вывод в /home/username/appium.log
.
И запускаем тесты с профилем linux-emulator-8.0:
Окей — локально, на моём ноутбуке, устройство запускается, всё работает.
А в чём проблема на билд-агенте? :-/
Запуск на билд-агенте
Запуск вручную
Попробуем подебажить.
Проверяем само устройство:
[simterm]
$ /android/Android/sdk/tools/bin/avdmanager list avd ... Available Android Virtual Devices: Name: Nexus_5X_API_26 Device: Nexus 5X (Google) Path: /home/project/.android/avd/Nexus_5X_API_26.avd Target: Google Play (Google Inc.) Based on: Android 8.0 (Oreo) Tag/ABI: google_apis_playstore/x86 Skin: 1080x1920 Sdcard: 100 MB
[/simterm]
И с помощью эмулятора:
[simterm]
$ emulator -list-avds Nexus_5X_API_26 test
[/simterm]
Пробуем его запустить на билд-агенте (т.к. запускаю по SSH — то с опцией -no-window
):
[simterm]
$ /android/Android/sdk/tools/emulator -avd Nexus_5X_API_26 -no-window I/O warning : failed to load external entity "/home/project/.AndroidStudio3.2/config/options/updates.xml" Couldn't statvfs() path: No such file or directory ERROR: Unable to access '/home/project/.emulator_console_auth_token': emulator console will not work I/O warning : failed to load external entity "/home/project/.AndroidStudio3.2/config/options/updates.xml" Your emulator is out of date, please update by launching Android Studio: - Start Android Studio - Select menu "Tools > Android > SDK Manager" - Click "SDK Tools" tab - Check "Android Emulator" checkbox - Click "OK"
[/simterm]
Окей, он есть, запускается.
Запускаем тесты:
[simterm]
$ mvn clean -Dmaven.test.failure.ignore=false test -P linux-emulator-8.0 [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building project-android-auto-tests 1.0 [INFO] ------------------------------------------------------------------------ ... [INFO] Running TestSuite ... ... TestNG 6.14.3 by Cédric Beust ([email protected]) ... Nov 21, 2018 6:18:38 PM io.appium.java_client.remote.AppiumCommandExecutor$1 lambda$0 INFO: Detected dialect: W3C ...
[/simterm]
И в логе Appium:
[simterm]
... [debug] [ADB] Trying to find Nexus_5X_API_26 emulator [debug] [ADB] Getting connected emulators [debug] [ADB] Getting connected devices... [debug] [ADB] 1 device(s) connected [debug] [ADB] 1 emulator(s) connected ...
[/simterm]
Устройство видно, Appium его находит.
Запуск из Jenkins
Значит — проблема в том, что при запуске автотестов из Jenkins — Appium не может запустить emulator
, при том, что вызов avdmanager list avd
и emulator -list-avds
список устройств возвращает нормально:
[simterm]
[Pipeline] sh + avdmanager list avd ... Android Virtual Devices: Name: Nexus_5X_API_26 Device: Nexus 5X (Google) Path: /home/project/.android/avd/Nexus_5X_API_26.avd Target: Google Play (Google Inc.) Based on: Android 8.0 (Oreo) Tag/ABI: google_apis_playstore/x86 Skin: 1080x1920 Sdcard: 100 MB --------- Name: test Path: /home/project/.android/avd/test.avd Target: Google APIs (Google Inc.) Based on: Android 7.1.1 (Nougat) Tag/ABI: google_apis/x86 [Pipeline] sh + emulator -list-avds Nexus_5X_API_26 test
[/simterm]
Осталось выяснить — почему Appium не может запустить девайс через эмулятор…
Ещё раз запускаем тесты без запущенного эмулятора, и смотрим лог Appium:
[simterm]
... [debug] [ADB] Emulator Nexus_5X_API_26 not running [ADB] [AVD OUTPUT] Fatal: QXcbConnection: Could not connect to display ((null):0, (null)) [ADB] [AVD OUTPUT] INFO: QtLogger.cpp:66: Fatal: QXcbConnection: Could not connect to display ((null):0, (null)) [ADB] Emulator avd Nexus_5X_API_26 exited with code null, signal SIGABRT ...
[/simterm]
А из-за чего появляется:
QXcbConnection: Could not connect to display ((null):0, (null))
?
Вот, судя по всему — она и является корнем проблемы.
Т.е. проблема возникает из-за того, что при запуске тестов через билд-агент, который установлен на Ubuntu, к которой подключен монитор, и на которой вручную тесты и эмулятор запускаются — билд-агент и/или Appium не могут увидеть дисплей.
Решения так и не нашлось — не помогло ни принудительное указание $DISPLAY
в переменных билд-агента, ни запуск эмулятора с опцией isHeadless
для Appium в pom.xml
:
... <isHeadless>true</isHeadless> ...
Добавллял avgArgs
:
... <avgArgs>-no-window</avgArgs>
Не помогло.
Задавал вопрос на Stackoverflow — но тоже без результата.
Костыль
Пока решения так и и не нашёл — применил костыль: запускаем девайс «вручную» перед запуском Appium.
Запуск тестов в Jenkins Pipeline джобе теперь выглядит так:
node('android') { stage('Run UI tests') { ws('/home/project/ANDROID_PROEJCT_QA') { sh '/usr/local/bin/appium -p 4723 --session-override > /home/project/appium.log &' sh "emulator -avd Nexus_5X_API_26 &> /home/project/emulator.log &" sleep 10 git branch: 'master', poll: false, url: '[email protected]:project-dev/ANDROID_PROJECT_QA.git' sh 'mvn clean -Dmaven.test.failure.ignore=false test -P linux-emulator-8.0' sh 'pkill -f appium || true' sh "adb -e emu kill" } } }
В строке emulator -avd Nexus_5X_API_26 &> /home/project/emulator.log &
— запускаем девайс, и в конце — adb -e emu kill
— убиваем его.
Так оно пока и работает.