visit
In the first part of this article, we described how to quickly and easily build an infrastructure for running UI tests on Android using Appium and Selenoid. We are continuing our story to explain how we incorporated the launch of UI tests in iOS into the process.
The maximum number of parallel workflows within a host is limited by its resources. Thus, we needed a tool for combining multiple hosts into one cluster. For this, we use the Go Grid Router (GGR) from the folks at Aerokube. Based on the description in the documentation, GGR is a load balancer used for creating scalable and highly available Selenium clusters.
Deploying GGR and GGR UI is easy:
mkdir -p /etc/grid-router/quota
.$ htpasswd -bc /etc/grid-router/users.htpasswd test test-password
.
$ cat /etc/grid-router/quota/test.xml
<qa:browsers xmlns:qa="urn:config.gridrouter.qatools.ru">
<browser name="android" defaultVersion="10.0" defaultPlatform="android">
<version number="10.0">
<region name="1">
<host name="0.0.0.0" port="4444" count="1"/>
</region>
</version>
</browser>
</qa:browsers>
docker run -d \
--name ggr \
-v /etc/grid-router/:/etc/grid-router:ro \
--net host aerokube/ggr:latest-release \
-listen=:4445 -guests-allowed
val driver = AndroidDriver(URL("//localhost:4445/wd/hub"), capabilities)
docker run -d \
--name ggr_ui \
-p 8888:8888 \
-v /etc/grid-router/quota:/etc/grid-router/quota:ro \
aerokube/ggr-ui:latest-release
Run the Selenoid UI container where we pass the GGR UI port via selenoid-uri
:
docker run -d \
--name selenoid-ui \
-p 4446:4446 \
--link selenoid:selenoid \
aerokube/selenoid-ui:1.10.4 \
--selenoid-uri "<//ggr-ui:8888>"
We were unable to replicate the structure used in running Android tests because we couldn’t find a way to run iOS simulators in Docker containers. One option that we considered was running , but we ran into doubts about the legality of its use for any purposes unrelated to . So, we decided to go a different route.
<qa:browsers xmlns:qa="urn:config.gridrouter.qatools.ru">
<browser name="android" defaultVersion="10.0" defaultPlatform="android">
<version number="10.0">
<region name="1">
<host name="0.0.0.0" port="4444" count="1"/>
</region>
</version>
</browser>
<browser name="iPhone 14" defaultVersion="16.2" defaultPlatform="iOS">
<version number="16.2">
<region name="1">
<host name="0.0.0.0" port="4723" count="1"/>
</region>
</version>
</browser>
</qa:browsers>
{
"iPhone 14": {
"default": "16.2",
"versions": {
"16.2": {
"image": ["appium", "--log-timestamp", "--log-no-colors", ...]
}
}
}
}
Give permission to execute the Selenoid file. In our case, we used chmod 755
.
Run Selenoid via the terminal. We used the following parameters: selenoid -conf ~/browsers.json -disable-docker -capture-driver-logs -service-startup-timeout 4m -session-attempt-timeout 4m -timeout 6m -limit 2
.
-limit
parameter was used to set the maximum number of running simulators. This is the reference value to be used by the GGR in the future. The performance of the host is used as a guide for setting the parameter.
{
"iPhone 14": {
"default": "16.2",
"versions": {
"16.2": {
"image": ["~/bin/config/start_appium.sh", "iPhone 14"]
}
}
}
}
#!/bin/bash
set -ex
DEVICE_NAME=$1
APPIUM_PORT=$(echo $2 | cut -d '=' -f 2)
function clean() {
if [ -n "$APPIUM_PID" ]; then
kill -TERM "$APPIUM_PID"
fi
if [ -n "$DEVICE_UDID" ]; then
xcrun simctl delete $DEVICE_UDID
fi
}
trap clean SIGINT SIGTERM
# Each simulator has a udid, so to run the same devices in parallel - clone and run
# only clones. You cannot clone a running device. After closing the session, delete the clone.
cloned_device_name="[APPIUM] ${DEVICE_NAME} ($(date +%Y%m%d%H%M%S))"
DEVICE_UDID=$(xcrun simctl clone "$DEVICE_NAME" "$cloned_device_name")
# //github.com/appium/appium-xcuitest-driver#important-simulator-capabilities
WDA_LOCAL_PORT=$(($APPIUM_PORT+1000))
MJPEG_SERVER_PORT=$(($WDA_LOCAL_PORT+1000))
DEFAULT_CAPABILITIES='"appium:udid":"'$DEVICE_UDID'","appium:automationName":"'XCUITest'","appium:wdaLocalPort":"'$WDA_LOCAL_PORT'","appium:mjpegServerPort":"'$MJPEG_SERVER_PORT'"'
appium --base-path=/wd/hub --port=$APPIUM_PORT --log-timestamp --log-no-colors --allow-insecure=get_server_logs,adb_shell \
--allow-cors --log-timestamp --log {choose_directory_for_logs} \
--default-capabilities "{$DEFAULT_CAPABILITIES}" &
APPIUM_PID=$!
wait
If the script is used, pay close attention to the stated capabilities and Appium startup settings. These are set up here assuming that Appium 2.x is used for the run ‒ Appium 1.x does not require the vendor to be specified in capabilities, and there is no option of specifying --base-pat.
When multiple Appiums are directly connected to GGR from one Mac mini, a problem arises with the emulators, because you cannot run the same emulator with identical UDIDs. You have to manually duplicate and hardcode the UDID each and every time. (For example, if you need to change the iOS version or the simulator model.)
Poor scalability. It is necessary to run Appium manually every time, and regularly check it for conflicts with ports and simulators.
Posted by Ivan Grigoriev.