visit
Hi all! My name is Taras Egorov; I'm an engineer at inDrive. I'll show you how we set up an infrastructure capable of running more than 5,000 tests per day on iOS and Android devices combined. The secret is simple: we used Selenoid.
browsers.json
config file:
{
"android": {
"default": "10.0",
"versions": {
"10.0": {
"image": "browsers/android:10.0",
"port": "4444",
"path": "/wd/hub"
}
}
}
}
The emulator image is specified in image
. The guys from have prepared ready-made images of Android emulators. You can check them out or . The images don't differ from each other in any way.
Let’s take the image browsers/android:10.0
as an example. The image must be downloaded beforehand: docker pull browsers/android:10.0
, otherwise the tests will not run:
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
You can check to see if Selenoid is working properly by following the link //localhost:4444 in your browser:
You are using Selenoid 1.10.7!
...
val driver = AndroidDriver(URL("//localhost:4444/wd/hub"), capabilities)
...
...
capabilities.setCapability("appium:app", "//storage.example.com/builds/app.apk")
...
...
capabilities.setCapability("appium:app", "/builds/app.apk")
...
Where /builds/app.apk
is the path inside the container where the emulator is being run. For this option to work properly, be sure to specify the volumes
in browsers.json
:
{
"android": {
"default": "10.0",
"versions": {
"10.0": {
...
"volumes": [
"/home/username/app.apk:/builds/app.apk:ro"
]
...
}
}
}
}
Where /home/username/app.apk
is the path to the build on the host platform.
./mvnw test
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] [//172.17.0.3:4444/wd/hub does not respond in 30s]
SERVICE_STARTUP_FAILED - Failed to start Docker container or driver binary
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
...
capabilities.setCapability("enableLog", true)
...
Run the tests and review the logs using the browser //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.
...
if [ -z "$VERBOSE" ]; then
APPIUM_ARGS="$APPIUM_ARGS --log-level error"
else
EMULATOR_ARGS="$EMULATOR_ARGS -verbose"
fi
...
To enable Appium logs, the parameters VERBOSE=true
and APPIUM_ARGS=--log-level debug
: must be passed to the container:
{
"android": {
"default": "10.0",
"versions": {
"10.0": {
...
"env": [
"VERBOSE=true",
"APPIUM_ARGS=--log-level debug"
]
...
}
}
}
}
Now it is enough to pass to APPIUM_ARGS=-log-level debug
.
...
[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 //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
...
docker run -d \
--name selenoid-ui \
-p 8080:8080 \
--link selenoid:selenoid \
aerokube/selenoid-ui:1.10.4 \
--selenoid-uri "//selenoid:4444"
Go to //0.0.0.0:8080 and open the Selenoid UI:
...
capabilities.setCapability("enableVNC", true)
capabilities.setCapability("enableVideo", true)
...
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
selenium
folder../automate_android.sh
and answer the questions. This is what it looks like in our case:
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 (e.g. "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
Push? >> n
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------