Hallo zusammen! Mein Name ist Taras Egorov; Ich bin Ingenieur bei inDrive. Ich zeige Ihnen, wie wir eine Infrastruktur aufbauen, die in der Lage ist, mehr als 5.000 Tests pro Tag auf iOS- und Android-Geräten zusammen durchzuführen. Das Geheimnis ist einfach: Wir haben Selenoid verwendet.
Letztes Jahr haben unsere Kollegen eine Studie zum Thema Autotests durchgeführt und wir haben im Rahmen der Studie an einer Umfrage teilgenommen.
Wir waren mit den Ergebnissen der Umfrage zufrieden und haben uns entschieden, einen Artikel zu schreiben, um unsere Erfahrungen mit Ihnen zu teilen und im Gegenzug einige Ratschläge zu erhalten. Wir hielten es für eine gute Idee, das Material in zwei Teile zu unterteilen: Der erste konzentriert sich auf Android und der zweite auf iOS.
Beginnen wir mit Android.
Selenoid ist ein beliebtes Tool zum Ausführen und Verwalten von Browsern und Android-Emulatoren in einem Docker-Container. Mehr dazu können Sie in der Dokumentation nachlesen.
Um Appium-Tests zu schreiben, verwenden wir:
browsers.json
Konfigurationsdatei:
{ "android": { "default": "10.0", "versions": { "10.0": { "image": "browsers/android:10.0", "port": "4444", "path": "/wd/hub" } } } }
Das Emulator-Image wird in image
angegeben. Die Jungs von aerokube haben vorgefertigte Bilder von Android-Emulatoren vorbereitet. Sie können sie sich hier oder hier ansehen. Die Bilder unterscheiden sich in keiner Weise voneinander.
Nehmen wir als Beispiel das Bild browsers/android:10.0
. Das Image muss vorher heruntergeladen werden: docker pull browsers/android:10.0
, sonst laufen die Tests nicht:
Original error: create container: Error response from daemon: No such image: browsers/android:10.0
docker run -d \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$(pwd)/selenoid/config/":/etc/selenoid/:ro \ -p 4444:4444 \ --name selenoid \ aerokube/selenoid:1.10.7
Sie können überprüfen, ob Selenoid ordnungsgemäß funktioniert, indem Sie dem Link http://localhost:4444 in Ihrem Browser folgen:
You are using Selenoid 1.10.7!
Geben Sie die Selenoid-Adresse in den Appium-Tests im Treiber an:
... val driver = AndroidDriver(URL("http://localhost:4444/wd/hub"), capabilities) ...
... capabilities.setCapability("appium:app", "https://storage.example.com/builds/app.apk") ...
Wenn Sie keinen Link bereitstellen können, können Sie den Pfad zum Build angeben:**
... capabilities.setCapability("appium:app", "/builds/app.apk") ...
Dabei ist /builds/app.apk
der Pfad innerhalb des Containers, in dem der Emulator ausgeführt wird. Damit diese Option ordnungsgemäß funktioniert, müssen Sie unbedingt die volumes
in browsers.json
angeben:
{ "android": { "default": "10.0", "versions": { "10.0": { ... "volumes": [ "/home/username/app.apk:/builds/app.apk:ro" ] ... } } } }
Dabei ist /home/username/app.apk
der Pfad zum Build auf der Hostplattform.
Das war's, wir haben Selenoid fast eingerichtet und können jetzt versuchen, die Tests durchzuführen:
./mvnw test
Leider können die Tests jedoch nicht ausgeführt werden. Schauen wir uns das an und sehen, was falsch ist.
Das erste, was Sie nach einem fehlgeschlagenen Start tun sollten, ist, die Selenoid-Protokolle zu überprüfen:
docker logs selenoid
[INIT] [Loading configuration files...] [INIT] [Loaded configuration from /etc/selenoid/browsers.json] [INIT] [Video Dir: /opt/selenoid/video] [INIT] [Your Docker API version is 1.41] [INIT] [Timezone: UTC] [INIT] [Listening on :4444] [NEW_REQUEST] [unknown] [172.17.0.1] [NEW_REQUEST_ACCEPTED] [unknown] [172.17.0.1] [LOCATING_SERVICE] [android] [10.0] [USING_DOCKER] [android] [10.0] [CREATING_CONTAINER] [selenoid/android:10.0] [STARTING_CONTAINER] [selenoid/android:10.0] [75e454341da7fc4b58ba104a5180813bac6cd7c124037a759b6c976e65b168fa] [CONTAINER_STARTED] [selenoid/android:10.0] [75e454341da7fc4b58ba104a5180813bac6cd7c124037a759b6c976e65b168fa] [0.40s] [0] [REMOVING_CONTAINER] [75e454341da7fc4b58ba104a5180813bac6cd7c124037a759b6c976e65b168fa] [0] [CONTAINER_REMOVED] [75e454341da7fc4b58ba104a5180813bac6cd7c124037a759b6c976e65b168fa] [0] [SERVICE_STARTUP_FAILED] [http://172.17.0.3:4444/wd/hub does not respond in 30s]
Wir sehen, dass der Status SERVICE_STARTUP_FAILED ist. Gehen Sie zur Dokumentation und sehen Sie sich den Statuswert an:
SERVICE_STARTUP_FAILED - Failed to start Docker container or driver binary
Der Fehler sagt Ihnen nicht viel und es sind weitere Informationen erforderlich. Es wäre gut, einen Blick auf die Containerprotokolle zu werfen. Machen wir das, indem wir die Protokollierung aktivieren:
docker run -d \ -p 4444:4444 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$(pwd)/selenoid/config/":/etc/selenoid/:ro \ -v "$(pwd)/selenoid/logs/":/opt/selenoid/logs/ \ aerokube/selenoid:1.10.7 \ -log-output-dir /opt/selenoid/logs
Wir aktivieren auch Protokolle im Abschnitt „Funktionen“:
... capabilities.setCapability("enableLog", true) ...
Führen Sie die Tests aus und überprüfen Sie die Protokolle mit dem Browser http://localhost:4444/logs/ :
2023-04-16T13:44:43.909768530Z Waiting X server... 2023-04-16T13:44:44.009494775Z Logging to: /tmp/fluxbox.log 2023-04-16T13:44:44.047587277Z Waiting X server... 2023-04-16T13:44:44.151933325Z Waiting X server... 2023-04-16T13:44:44.262850410Z * daemon not running; starting now at tcp:5037 2023-04-16T13:44:44.457972956Z * daemon started successfully 2023-04-16T13:44:44.458249266Z adb: no devices/emulators found 2023-04-16T13:44:45.463480812Z adb: no devices/emulators found 2023-04-16T13:44:46.471547723Z adb: no devices/emulators found 2023-04-16T13:44:47.476093515Z adb: no devices/emulators found 2023-04-16T13:44:48.481987351Z adb: no devices/emulators found 2023-04-16T13:44:49.486503149Z adb: no devices/emulators found 2023-04-16T13:44:50.492757801Z adb: no devices/emulators found 2023-04-16T13:44:51.499094108Z adb: no devices/emulators found 2023-04-16T13:44:52.505862428Z adb: no devices/emulators found 2023-04-16T13:44:53.513276412Z adb: no devices/emulators found 2023-04-16T13:44:54.520642210Z adb: no devices/emulators found 2023-04-16T13:44:55.527420189Z adb: no devices/emulators found 2023-04-16T13:44:56.534631013Z adb: no devices/emulators found 2023-04-16T13:44:57.316094939Z WARNING. Using fallback path for the emulator registration directory. 2023-04-16T13:44:57.335415397Z checkValid: hw configs not eq 2023-04-16T13:44:57.541959741Z adb: device offline 2023-04-16T13:44:58.547907700Z adb: device offline 2023-04-16T13:44:58.565504866Z emulator: WARNING: System image is writable 2023-04-16T13:44:58.565528396Z emulator: Cold boot: different AVD configuration 2023-04-16T13:44:58.565532576Z Your emulator is out of date, please update by launching Android Studio: 2023-04-16T13:44:58.565536346Z - Start Android Studio 2023-04-16T13:44:58.565539506Z - Select menu "Tools > Android > SDK Manager" 2023-04-16T13:44:58.565543076Z - Click "SDK Tools" tab 2023-04-16T13:44:58.565546216Z - Check "Android Emulator" checkbox 2023-04-16T13:44:58.565549216Z - Click "OK" 2023-04-16T13:44:58.565552146Z 2023-04-16T13:44:59.554451514Z adb: device offline 2023-04-16T13:45:00.560926060Z adb: device offline 2023-04-16T13:45:01.568777440Z adb: device offline 2023-04-16T13:45:12.124226047Z emulator: INFO: boot completed 2023-04-16T13:45:12.124251007Z emulator: INFO: boot time 27848 ms 2023-04-16T13:45:12.124255077Z emulator: Increasing screen off timeout, logcat buffer size to 2M. 2023-04-16T13:45:12.152557294Z emulator: Revoking microphone permissions for Google App.
Auch hier helfen die Container-Logs nicht weiter, da man die Appium-Logs nicht sehen kann. Versuchen wir nun, sie zu aktivieren. Schauen wir uns dazu den Skript- Einstiegspunkt point.sh an:
... if [ -z "$VERBOSE" ]; then APPIUM_ARGS="$APPIUM_ARGS --log-level error" else EMULATOR_ARGS="$EMULATOR_ARGS -verbose" fi ...
Um Appium-Protokolle zu aktivieren, müssen die Parameter VERBOSE=true
und APPIUM_ARGS=--log-level debug
: an den Container übergeben werden:
{ "android": { "default": "10.0", "versions": { "10.0": { ... "env": [ "VERBOSE=true", "APPIUM_ARGS=--log-level debug" ] ... } } } }
Damit Appium Protokolle debuggen kann, müssen Sie VERBOSE übergeben. In diesem Fall schalten sich die Emulatorprotokolle ein und beginnen mit dem Füllen des „Äthers“. Aber wir haben das für zukünftige Bilder behoben =)
Jetzt reicht es, an APPIUM_ARGS=-log-level debug
zu übergeben.
... [HTTP] --> POST /wd/hub/session/c89fa9c2-ca2b-49cd-ab31-590eeccf77d1/element [HTTP] {"using":"id","value":"authorization_edittext_phone"} [debug] [W3C (c89fa9c2)] Calling AppiumDriver.findElement() with args: ["id","authorization_edittext_phone"," c89fa9c2-ca2b-49cd-ab31-590eeccf77d1"] [debug] [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -and roid uiautomator [debug] [BaseDriver] Waiting up to 0 ms for condition [debug] [WD Proxy] Matched '/element' to command name 'findElement' [debug] [WD Proxy] Proxying [POST /element] to [POST http://127.0.0.1:8200/wd/hub/session/65943f03-3b35-4d3eb221-d6dc7988f935/element] with body: {"strategy":"id","selector": "authorization_edittext_phone","context":"","multiple":false} [WD Proxy] Got response with status 404: {"sessionId":"65943f03-3b35-4d3e-b221-d6dc7988f935","value":{"error" :"no such element","message":"An element could not be located on the page using the given search parameters","stacktrace":"io.appium.uiautomator2.common.exceptions.El ementNotFoundException: An element could not be located on the page using the given search parameters\n\tat io.appium.uiautomator2.handler.FindElement.safeHandle(Find Element.java:73)\n\tat io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:41)\n\tat io.appium.uiautomator2.server.AppiumServlet. handleRequest(AppiumServlet.java:253)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:247)\n\tat io.appium.uiautomator2.http.Se rverHandler.channelRead(ServerHandler.java:68)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)\n\tat io .netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)\n\tat io.netty.chann... [debug] [W3C] Matched W3C error code 'no such element' to NoSuchElementError [debug] [W3C (c89fa9c2)] Encountered internal error running command: NoSuchElementError: An element could not be located on the page using the given search parameters. [debug] [W3C (c89fa9c2)] at AndroidUiautomator2Driver.findElOrEls (/opt/node_modules/appium/node_modules/appium-android-driver/lib/commands/find.js:75:11) [debug] [W3C (c89fa9c2)] at process._tickCallback (internal/process/next_tick.js:68:7) [HTTP] <-- POST /wd/hub/session/c89fa9c2-ca2b-49cd-ab31-590eeccf77d1/element 404 23 ms - 444 ...
Wie aus den Protokollen hervorgeht, kann Appium unser Element nicht finden. Mal sehen, was auf dem Emulatorbildschirm passiert. Dazu müssen wir die Selenoid-Benutzeroberfläche ausführen:
docker run -d \ --name selenoid-ui \ -p 8080:8080 \ --link selenoid:selenoid \ aerokube/selenoid-ui:1.10.4 \ --selenoid-uri "http://selenoid:4444"
Gehen Sie zu http://0.0.0.0:8080 und öffnen Sie die Selenoid-Benutzeroberfläche:
Stellen Sie sicher, dass Sie in den Tests VNC und Videoaufzeichnung aktiviert haben:
... capabilities.setCapability("enableVNC", true) capabilities.setCapability("enableVideo", true) ...
Der Selenoid-Startbefehl sieht am Ende so aus:
docker run -d \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "(pwd)/selenoid/logs/":/opt/selenoid/logs/ \ -v /opt/selenoid/video/:/opt/selenoid/video/ \ -e OVERRIDE_VIDEO_OUTPUT_DIR="/opt/selenoid/video/" \ -p 4444:4444 \ -name selenoid \ aerokube/selenoid:1.10.7 \ -log-output-dir /opt/selenoid/logs
Öffnen Sie die Selenoid-Benutzeroberfläche, sobald die Tests ausgeführt werden:
Und hier ist ein Video davon .
Wir haben die Ursache für den Startfehler gefunden. Großartig! Lass uns weitermachen.
Wie sich herausstellt, funktionieren die Bilder von Selenoid-Emulatoren ohne Google Play-Dienste nicht. Um dieser Situation abzuhelfen, müssen Sie selbst ein Emulator-Image erstellen. Die Jungs von aerokube haben alles zusammengestellt, was Sie dafür brauchen: ein Repository mit Fotos und Dokumentation .
selenium
Ordner../automate_android.sh
aus und beantworten Sie die Fragen. So sieht es in unserem Fall aus:
Specify Appium version: [1.18.1] >> 1.18.1 Specify Android image type (possible values: "default", "google_apis", "google_apis_playstore", "android-tv", "android-wear"): [default] >> google_apis Specify Application Binary Interface (possible values: "armeabi-v7a", "arm64-v8a", "x86", "x86_64"): [x86] >> x86 Specify Android version: [8.1] >> 10.0 Specify device preset name if needed (eg "Nexus 4"): >> Specify SD card size, Mb: [500] >> Specify userdata.img size, Mb: [500] >> Are you building a Chrome Mobile image (for mobile web testing): [n] >> y Specify Chromedriver version if needed (required for Chrome Mobile): >> 74.0.3729.6 Specify image tag: [selenoid/chrome-mobile:74.0] >> android-emulator:10.0 Add Android quick boot snapshot? [y] >> n
Als ich die Frage „Android-Schnellstart-Snapshot hinzufügen?“ sah, dachte ich, es sei ein Emulator- Snapshot . Aber wenn man sich den Code ansieht, ruft er ein Skript auf, das zum Installieren von APK-Anwendungen benötigt wird. Im Grunde bringt es uns keinen Gewinn.
Wir verwenden bereits Snapshot-Emulatoren , um den Start von Containern zu beschleunigen , aber wir werden in anderen Artikeln darüber sprechen.
Sobald das Image erstellt ist, werden wir aufgefordert, es in die Registrierung zu übertragen. Da wir das noch nicht benötigen, lehnen wir das Angebot freundlicherweise ab:
Push? >> n
Wir haben einen Build mit Google Play-Diensten zusammengestellt. Denken Sie daran, den Bildparameter in browsers.json zu ändern und Selenoid neu zu starten.
Versuchen wir nun, die Tests noch einmal durchzuführen:
[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
Und hier ist ein Video vom Testlauf.
Was wir gemacht haben:
Ich möchte Ihnen noch etwas mitteilen:
Im nächsten Teil erzählen wir Ihnen, wie wir die Infrastruktur erweitert und Tests unter iOS durchgeführt haben.