appium-uiautomator2-server 是Appium在安卓端实现自动化能力的核心,上层脚本的所有操作,最终都转换成基于 appium-uiautomator2-server 提供的HTTP接口进行调用。本篇讲述该模块的调试方法,帮助读者更好的理解Appium在安卓上是如何实现自动化的。
1.uiautomator与uiautomator2-server的关系
UI Automator 是google官方提供的一个用于编写自动化测试的官方库,其优势主要为:
官方目前大力支持的框架之一(另外一个是Expresso),
基于安卓原生控件解析,比坐标类型的兼容性更好
提供了丰富的等待机制
不依赖源码(纯黑盒)
可以跨应用。
市面上的诸多安卓测试工具(Appium,Minitest,ATX)等基本都绕不开对uiautomator库的依赖。
但如果直接使用uiautomator进行自动化测试,需要编写原生的java或kotlin代码,成本较高。uiautomator2-server
的作用,就是对uiautomator的API做进一步的封装,将UI自动化常用的操作,通过HTTP接口的方式提供出来。使用者只需要把Appium Uiautomator2 Server在手机上运行起来,就可以通过给它发送各种HTTP请求,驱动自动化流程的进行。也正是得益于uiautomator2-server
这层HTTP服务封装,Appium的上层脚本才可以支持多种编程语言(Ruby,Python,JS,Java),最终都会把上层业务的各种指令转换成uiautomator2-server
的接口请求。
为方便学习,本文配套工程链接 以源代码形式引入了uiautomator,在config.gradle
文件中,将useRemoteSDKLibrary
设置为false即为源码依赖模式。
2.调试Appium自带的APK
Appium官方针对Appium Uiautomator2 Server
编写了一系列的E2E测试代码,并提供了一个单独的APK。
测试工程将该apk文件放到了项目的目录下,并修改了运行脚本。
androidTest目录下的用例,可以直接点红框的按钮运行起来。覆盖了Appium Uiautomator2 Server
基本的接口调用。
也可以在代码中设置断点,以debug的方式单步调试运行。
3.调试设备上的其他应用
由于Appium Uiautomator2 Server
编写的E2E测试代码,会默认拉起ApiDemos-debug.apk
应用。如果期望验证其他的应用,可以通过设置 配置文件 中的 runApiDemoApk
字段为false。
后续的E2E测试用例将不再拉起默认的应用,可以自己编写针对目标App的测试代码。
4.基于HTTP请求的方式运行Appium Uiautomator2 Server
(1) 打包Appium Uiautomator2 Server 工程
在项目根目录下运行打包命令:
bash
./gradlew clean assembleServerDebug assembleServerDebugAndroidTest
注意打包环境的jdk版本必须>=11以上,最终的打包产物为如下的两个apk:
正式安装之前,可以将之前的e2e相关测试apk卸载掉,执行如下命令:
lua
adb uninstall io.appium.uiautomator2.server.test
adb uninstall io.appium.uiautomator2.server
adb uninstall io.appium.uiautomator2.e2etest.test
adb uninstall io.appium.uiautomator2.e2etest
adb uninstall io.appium.espressoserver.test
之后通过 adb install -r
将上面的两个产物apk完成路径拼入,执行安装即可。
(2) 启动 Appium Uiautomator2 Server 服务
step1:本地先做端口转发:
adb forward tcp:65272 tcp:6790
其中 65272 可以换为其他的端口号,不冲突即可。
step2:控制台基于instrument启动服务
bash
adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner
(3) 给服务发送请求
(1) 建立连接
json
curl -d '{"capabilities":{}}' -X POST "http://127.0.0.1:65272/session"
返回:
erlang
{"sessionId":"ccf6f9e5-4e53-41a3-8212-61ba80a23ed4","value":{"capabilities":{},"sessionId":"ccf6f9e5-4e53-41a3-8212-61ba80a23ed4"}}%
表示成功建立了一个会话连接请求。
这里定义变量记录下sessionID,方便后续进一步的调试
ini
CUR_SESSION_ID='ccf6f9e5-4e53-41a3-8212-61ba80a23ed4'
(2) 获取视图操作
bash
curl -X GET "http://127.0.0.1:65272/session/$CUR_SESSION_ID/source"
输出即为当前界面的完整xml结构。
(3) 滑动操作
json
curl -d '{"endY":200,"endX":540,"startY":1710,"startX":540,"steps":100}' -X POST "http://127.0.0.1:65272/session/$CUR_SESSION_ID/touch/perform"
触发一个滑动的操作,[startX, startY]
,[endX,endY]
分别表示滑动手势的开始位置与结束位置。step用于控制滑动操作整体的耗时。steps设置为100表示在0.5秒内完成。
(4) 查找操作
json
curl -d '{"selector":"更多信息","strategy":"accessibility id"}' -X POST "http://127.0.0.1:65272/session/$CUR_SESSION_ID/elements"
会查找当前界面 accessibility id
为更多信息
的元素,结果为:
erlang
{"sessionId":"ccf6f9e5-4e53-41a3-8212-61ba80a23ed4","value":[{"ELEMENT":"00000000-0000-03af-ffff-ffff0000039a","element-6066-11e4-a52e-4f735466cecf":"00000000-0000-03af-ffff-ffff0000039a"}]}%
value部分为一个数组,表示查找到了目标元素信息。可以把元素id缓存起来:
ini
CUR_ELELMENT_ID='00000000-0000-03af-ffff-ffff0000039a'
之后基于元素id,实现点击的操作:
bash
curl -X POST "http://127.0.0.1:65272/session/$CUR_SESSION_ID/element/$CUR_ELELMENT_ID/click"
当前界面 accessibility id
为更多信息
的元素就会被点击。
(5) 其他更多操作
Appium-Server的所有接口定义,均在AppiumServlet.java 文件中,按照上述的思路,拼接指定的参数,即可完成各个操作的测试流程。