背景:
偶现ANR&Crash问题难以复现,可以通过mokey,UIAutomator2做复现,解放双手提高生产力,其中后者操作控制可编程。相比于安卓多种单元测试框架,无代码侵入,可直接应对二进制文件自动化测试。使用一下,整个小案列,顺便看下源码实现。
Install uiautomator2
git clone https://github.com/openatx/uiautomator2
pip install -e uiautomator2
测试是否安装成功 `uiautomator2 --help`
Install weditor (UI Inspector)
pip install -U weditor
pip install weditor 出错|跳过
pip install -U setuptools
git clone https://github.com/openatx/weditor
pip3 install -e weditor
Install daemons to a device
python -m uiautomator2 init
启动weditor
python -m weditor
将此命令做成bat,点击执行
使用weditor
界面&编辑器
uiautomator的api使用
略,python脚本编写,详见官方文档
自动化测试案例举个例
app启动过程偶现ANR问题,自动化复现,获取日志
# coding: utf-8
#
import uiautomator2 as u2
import time
import logging
d = u2.connect()
d.click_post_delay = 1.5#
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(funcName)s - %(message)s')
logger = logging.getLogger(__name__)
logger.info(d)
d.watcher.stop()
d.app_stop_all()
d.watcher("ANR").when(xpath="ANR").when("Force Close").click()
appId = "xxx"
def run_app():
if d.session(appId, attach=True):
logger.info("已打开程序")
time.sleep(2)
if d(text="xxx").exists(timeout=5):
logger.info("确认启动完成")
time.sleep(2)
else:
logger.info("不确定启动完成")
time.sleep(5)
d.app_stop(appId)
else:
print('打开程序失败')
i=0
while True:
print('------',i,'------')
run_app()
if i>5000:
break
i+=1
d.shell("adb bugreport")
做成exe分发出去
pyinstaller -F -w -i .\icon_Cow.ico .\main.py
启动时间统计
自动化打卡
...
UiAutomator
1. uiautomator是命令行jar包
2. 通过accessibility获取到窗口视图节点信息树,转换成xml序列化数据文件到指定目录
3. 视图树信息获取流程,有分层,跨进程,涉及到的类如下
- com.android.commands.uiautomator.Launcher
- com.android.commands.uiautomator.DumpCommand
- com.android.uiautomator.core.UiAutomationShellWrapper
- android.app.UiAutomation
- android.app.UiAutomationConnection
- android.app.UiAutomation$IAccessibilityServiceClientImpl
- android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper
- com.android.server.accessibility.UiAutomationManager
- com.android.server.accessibility.AccessibilityServiceConnection
- android.accessibilityservice.AccessibilityService
- android.view.accessibility.AccessibilityInteractionClient
- com.android.server.accessibility.AbstractAccessibilityServiceConnection
- android.view.accessibility.AccessibilityWindowInfo
- android.view.accessibility.AccessibilityNodeInfo
4. 剩下的主要api
- UiAutomatorBridge取代了之前版本UIXXX作为上下文
- InteractionController 拦截控制器,可以处理各种点击触摸按键滑动,支持等待,对应UIXXX相应功能。
- QueryController窗口视图树节点查询能力
UiAutomatorViewer源码原理分析
1. 命令行jar包&eclipse桌面应用,提供弹窗选设备,展示视图树(rectangles+snapshot)
2. ddmlib连接设备,通过shell命令获取ui—hierarchy.xml文件及snapshot,xml—SAX解析填充方式矩形框到swt的treeview+一张snapshot。
待补充
android-uiautomator-server源码分析
命令方式远程启动安卓测试进程,测试进程顺带拉起app监控进程。
1. 命令方式启动安卓进程和组件
adb forward tcp:9008 tcp:9008
adb shell am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub \
com.github.uiautomator.test/androidx.test.runner.AndroidJUnitRunner
adb shell am broadcast -a ....此命令可以发送广播,被应用接收到,转成端测业务处理逻辑
adb shell am start -n ...
---app进程app-uiautomator.apk
2. 对外提供的服务:向PC上报(http请求方式)电量wif变更(广播监听方式触发),录屏、拍照、旋转屏幕监听、修改手机GPS&WIFI定位,远程触摸事件控制。还有最重要的远程暴露UiAutomator能力接口,rpc方式见8。
3. 访问PC服务:起停检查测试uiautomator服务,提供端侧提示语。
4. app端侧使用:端侧点击按钮,悬浮窗。
5. 有4个main函数,调用举例:
APKPATH=$(adb shell pm path com.github.uiautomator | cut -d: -f2)
adb shell CLASSPATH=$APKPATH exec app_process /system/bin com.github.uiautomator.MinicapAgent --help
6、动态权限通过反射处理
7. reflect、commons.cli、NanoHTTPD、MediaCodec,jsonrpc4j(8)等应用
----测试进程app-uiautomator-test.apk
8. 测试目录下有Stub启动rpc服务(json协议)(AutomatorHttpServer:NanoHTTPD)AutomatorService,对远程暴露UiAutomator的api或能力(用的还是UIXXXAPI),是以rpc方式提供的。
待补充
UIAutomator2源码原理分析
1. py实现的映射库,PC端通过远程rpc(json协议),映射UIAutomator能力的api。
2. usb或wifi方式连接设备为起点,Device(abc多继承,3~7)封装了所有能力。
3. _Device建立jsonrpc远程服务连接,通过http请求封装了主要的api
4. _AppMixIn封装app开头的几个常用命令
5. _PluginMixIn封装xpath,录屏,拍照,控件,等待
6. _InputMethodMixIn输入事件
7. _DeprecatedMixIn略
8. 2的流程连接设备,通过rpc启动手机端服务,如下:
connect(addr=None) -> Device:
def connect_usb(serial: Optional[str] = None, init: bool = False) -> Device:
def connect_adb_wifi(addr) -> Device:
class Device(_Device, _AppMixIn, _PluginMixIn, _InputMethodMixIn, _DeprecatedMixIn):
class _Device(_BaseClient):
def __init__(self, clnt: "_BaseClient"):
return self.server._jsonrpc_retry_call(self.method, params,
self.reset_uiautomator(str(e)) # uiautomator可能出问题了,强制重启一下
卸载安装app-uiautomator.apk和app-uiautomator-test.apk
output = self._test_run_instrument()
"am instrument -w -r -e debug false -e class com.github.uiautomator.stub.Stub com.github.uiautomator.test/android.support.test.runner.AndroidJUnitRunner",
def _test_run_instrument(self):
9. 也提供cmd命令调用,帮助,通过adb_util库提供也有些调用,安装apk,获取手机配置信息,但大部分uiautomator能力是通过通过http或httprpc的。
待补充
weditor源码原理分析
命令行应用,通过浏览器访问自建立web应用,通过UIAutomator2或wda获取窗口视图树信息,展示到web控件树种,提供交互操作。
用浏览器访问本地页面,创建本地web应用,使用同一个端口号。
webbrowser.open(f'http://localhost:{port}', new=2)
application = make_app({
'static_path': os.path.join(__dir__, 'static'),
'template_path': os.path.join(__dir__, 'templates'),
'debug': debug,
})
application.listen(port)
通过tornado的web服务器api创建web应用(有点像netty的api设计)
application = tornado.web.Application(
[
(r"/", MainHandler),
(r"/api/v1/version", VersionHandler),
(r"/api/v1/connect", DeviceConnectHandler),
(r"/api/v1/crop", CropHandler),
(r"/api/v1/devices/([^/]+)/screenshot", DeviceScreenshotHandler),
(r"/api/v1/devices/([^/]+)/hierarchy", DeviceHierarchyHandler),
# (r"/api/v1/devices/([^/]+)/exec", DeviceCodeDebugHandler),
(r"/api/v1/devices/([^/]+)/widget", DeviceWidgetListHandler),
(r"/api/v1/widgets", DeviceWidgetListHandler), # add widget
(r"/api/v1/widgets/([^/]+)", DeviceWidgetListHandler),
# v2
(r"/api/v2/devices/([^/]+)/hierarchy", DeviceHierarchyHandlerV2),
# widgets
(r"/widgets/([^/]+)", WidgetPreviewHandler),
(r"/widgets/(.+/.+)", tornado.web.StaticFileHandler, {
"path": "./widgets"
}),
# cache static assets
(r"/(unpkg.com/.*)", StaticProxyHandler),
(r"/(cdn.jsdelivr.net/.*)", StaticProxyHandler),
# (r"/ws/v1/build", BuildWSHandler),
(r"/ws/v1/python", PythonShellHandler),
(r"/quit", QuitHandler),
],
**settings)
可视化可交互页面部分内容分析
MainHandler加载了一个本地模板页面
DeviceConnectHandler,处理设备连接,device支持安卓ios,支持snapshot,窗口视图树信息获取。_AndroidDevice实际上用到是UIAutomator2,视图树信息获取将xml文件转成json数据。_AppleDevice用到的是wda,视图树信息直接是json。
DeviceHierarchyHandlerXXX就是提供视图树信息的调用。
DeviceWidgetListHandler会同步修改widget_data、meta.json、hierarchy.xml(get获取,put删除,post提交)应该是视图树节点展示修改删除。
DeviceScreenshotHandler提供手机snapshot
XXXHandler略。
待补充
参考文档
blog.csdn.net/u011106915/… github.com/openatx/uia… blog.csdn.net/hzk59451232… blog.csdn.net/hzk59451232… blog.csdn.net/hzk59451232… testerhome.com/topics/1135… testerhome.com/topics/1103… blog.csdn.net/cadi2011/ar…