Проблема: 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 — убиваем его.
Так оно пока и работает.
