이 기사의 첫 번째 부분에서는 Appium 및 Selenoid를 사용하여 Android에서 UI 테스트를 실행하기 위한 인프라를 빠르고 쉽게 구축하는 방법을 설명했습니다. 우리는 iOS에서 UI 테스트 출시를 프로세스에 어떻게 통합했는지 설명하기 위해 이야기를 계속하고 있습니다.
호스트 내의 최대 병렬 워크플로 수는 해당 리소스에 따라 제한됩니다. 따라서 여러 호스트를 하나의 클러스터로 결합하기 위한 도구가 필요했습니다. 이를 위해 Aerokube 직원의 GGR( Go Grid Router )을 사용합니다. 문서의 설명에 따르면 GGR은 확장 가능하고 가용성이 높은 Selenium 클러스터를 만드는 데 사용되는 로드 밸런서입니다.
테스트가 포함된 프로젝트는 사용된 프로세스의 일부로 GGR에서 쿼리를 실행합니다. 구성에 지정된 셀레노이드 매개변수를 폴링하고 사용된 플랫폼, 자유 흐름의 가용성 및 각 셀레노이드의 사전 정의된 특정 중량을 기반으로 부하를 분산시킵니다.
GGR 및 GGR UI 배포는 쉽습니다.
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>
GGR 컨테이너를 실행합니다.
docker run -d \ --name ggr \ -v /etc/grid-router/:/etc/grid-router:ro \ --net host aerokube/ggr:latest-release \ -listen=:4445 -guests-allowed
테스트 프로젝트에서 Appium 포트를 실행 중인 GGR의 포트로 변경합니다.
val driver = AndroidDriver(URL("http://localhost:4445/wd/hub"), capabilities)
GGR UI 컨테이너를 실행합니다.
docker run -d \ --name ggr_ui \ -p 8888:8888 \ -v /etc/grid-router/quota:/etc/grid-router/quota:ro \ aerokube/ggr-ui:latest-release
selenoid-uri
통해 GGR UI 포트를 전달하는 Selenoid UI 컨테이너를 실행합니다.
docker run -d \ --name selenoid-ui \ -p 4446:4446 \ --link selenoid:selenoid \ aerokube/selenoid-ui:1.10.4 \ --selenoid-uri "<http://ggr-ui:8888>"
이제 Selenoid UI는 GGR에 연결된 모든 Selenoid 클러스터의 상태를 표시해야 합니다.
우리는 자체 Mac mini 팜을 사용하여 iOS에서 UI 테스트를 실행합니다. 팜은 폐기되었지만 작동 중인 MacBook으로 유사하게 조립할 수 있습니다. 또는 대여 하실 수도 있습니다. 각 호스트에 다음을 설치해야 합니다.
Docker 컨테이너에서 iOS 시뮬레이터를 실행하는 방법을 찾을 수 없었기 때문에 Android 테스트 실행에 사용된 구조를 복제할 수 없었습니다. 우리가 고려한 옵션 중 하나는 Docker-OSX를 실행하는 것이었지만 OS X 보안 연구 와 무관한 목적으로 사용하는 것이 합법성에 대한 의구심에 부딪혔습니다. 그래서 우리는 다른 길을 가기로 결정했습니다.
이전에 생성된 GGR 구성 파일에 iOS 테스트용 Selenoid 호스트로 Appium(포트 4723)을 추가했습니다.
<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>
이러한 경우 iOS 구성표는 다음과 같습니다.
이 반복에 사용된 구조는 작동 가능합니다. 문제는 이 경우 각 Mac mini에서 하나의 워크플로에서만 테스트를 실행할 수 있다는 점입니다. 이는 낭비입니다. 또한 클러스터는 Selenoid UI에 표시되지 않습니다.
Selenoid를 사용하면 단순한 컨테이너 이상의 작업을 수행할 수 있습니다. 위의 문제는 iOS 테스트를 실행할 때 Selenoid를 실행 파일로 사용하기로 한 결정에 영향을 미쳤습니다.
browsers.json 구성 파일을 만듭니다.
구성 파일에서 Appium 및 시작 설정을 지정해야 합니다.
{ "iPhone 14": { "default": "16.2", "versions": { "16.2": { "image": ["appium", "--log-timestamp", "--log-no-colors", ...] } } } }
Selenoid 파일 실행 권한을 부여합니다. 우리의 경우 chmod 755
사용했습니다.
터미널을 통해 Selenoid를 실행합니다. 우리는 다음 매개변수를 사용했습니다: selenoid -conf ~/browsers.json -disable-docker -capture-driver-logs -service-startup-timeout 4m -session-attempt-timeout 4m -timeout 6m -limit 2
.
-limit
매개변수는 실행 중인 시뮬레이터의 최대 수를 설정하는 데 사용되었습니다. 이는 향후 GGR에서 사용할 참조값입니다. 호스트의 성능은 매개변수 설정을 위한 지침으로 사용됩니다.필요한 경우 갑작스러운 시스템 재시작 시 Selenoid를 자동 실행하기 위해 각 Mac mini에 PLIST 파일을 생성할 수 있습니다.
이제 클러스터 프로세스는 다음과 같습니다.
이 접근 방식을 통해 우리는 Selenoid UI 기능과 동일한 호스트의 여러 흐름에서 테스트를 실행하는 기능을 부분적으로 달성합니다.
단점은 각 Mac mini에서 시뮬레이터를 생성하고 UUID 및 포트 할당을 지정하여 이를 Appium과 연결하기 위해 여러 일상적인 작업을 수동으로 수행해야 한다는 것입니다. 나중에 새로운 iOS 버전으로 업그레이드해야 하는 경우 문제가 될 수 있습니다.
우리는 시간이 지남에 따라 계속 성장할 대규모 Mac mini 팜을 보유하고 있습니다. 이를 염두에 두고 우리는 시뮬레이터를 직접 만든 다음 Appium에 연결할 필요가 없도록 확장을 더 쉽게 만드는 방법을 찾고 있었습니다. 이전 스키마를 적용했다면 Appium과 시뮬레이터의 수명이 길어져 예측할 수 없는 결과가 발생할 수 있었습니다.
솔루션을 검색하면서 우리는 bash 스크립트가 Selenoid 구성 파일에서 호스트로 지정될 수 있음을 발견했습니다.
{ "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") # https://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
스크립트를 사용하는 경우 명시된 기능과 Appium 시작 설정에 세심한 주의를 기울이십시오. Appium 2.x가 실행에 사용된다는 가정 하에 여기에서 설정됩니다. Appium 1.x에서는 기능에 공급업체를 지정할 필요가 없으며 --base-pat를 지정하는 옵션이 없습니다.
이 스크립트는 병렬로 실행되는 시뮬레이터의 문제를 해결합니다.
여러 Appium이 하나의 Mac mini에서 GGR에 직접 연결된 경우 동일한 UDID로 동일한 에뮬레이터를 실행할 수 없기 때문에 에뮬레이터에 문제가 발생합니다 . 매번 UDID를 수동으로 복제하고 하드코딩해야 합니다. (예를 들어 iOS 버전이나 시뮬레이터 모델을 변경해야 하는 경우입니다.)
확장성이 좋지 않습니다 . 매번 Appium을 수동으로 실행하고 정기적으로 포트 및 시뮬레이터와의 충돌을 확인해야 합니다.
Selenoid를 사용하면 단일 호스트 내의 여러 Appium + Simulator 쌍 간에 충돌을 일으키지 않는 단일 스크립트로 이 프로세스를 단순화할 수 있습니다. Appium을 시작하고 Selenoid로부터 해당 신호를 수신하면 이를 종료하며, 시작 시 시뮬레이터를 동적으로 복제하고 세션이 끝나면 삭제합니다.
우리가 개발한 프로세스는 다음과 같습니다.
다음으로, 각 Mac mini의 Selenoid 주소를 배포된 GGR의 구성 파일에 추가하여 Android와 iOS 구조를 병합합니다.
조립된 인프라를 통해 두 플랫폼 모두에서 36개 워크플로에 걸쳐 시간당 총 약 500개의 UI 테스트를 실행할 수 있습니다. Android 테스트를 위한 새 호스트 추가는 GitHub Actions의 워크플로를 사용하여 완전히 자동화할 수 있으며 약 2분 정도 소요됩니다. 가까운 시일 내에 Mac mini에도 Selenoid 클러스터 배포를 자동화할 계획입니다.
더 나아가, Linux가 설치된 Mac mini에서 Docker-OSX 컨테이너를 실행하여 모든 프로세스를 통합하고 MacOS 사용 규칙을 위반하지 않고 더 쉽게 배포할 수 있도록 하려고 합니다. 관련 경험이 있으신 경우 댓글로 공유해 주시면 감사하겠습니다.
게시자: Ivan Grigoriev .