一、背景简介

Google 官方提供了一个 Android 自动化测试工具(Java 库),基于 Accessibility 服务,功能很强,可以对第三方 App 进行测试,获取屏幕上任意一个 App 的任意一个控件属性,并对其进行任意操作,但有两个缺点:

测试脚本只能使用 Java 语言;
测试脚本要打包成 jar 或者 apk 包上传到设备上才能运行;
实际工作中,我们希望测试逻辑能够用 Python 编写,能够在电脑上运行的时候就控制手机。所以基于这个目的开发了 python-uiautomator2 自动化测试开源工具,其封装了谷歌自带的 uiautomator2 测试框架,可以运行在支持 Python 的任一系统上,目前版本为 V2.10.2。

GitHub 开源地址:

https://github.com/openatx/uiautomator2

二、工作原理

如图所示,python-uiautomator2 主要分为两个部分,python 客户端,移动设备

python 端: 运行脚本,并向移动设备发送 HTTP 请求;
移动设备:移动设备上运行了封装了 uiautomator2 的 HTTP 服务,解析收到的请求,并转化成 uiautomator2 的代码;
整个过程:

在移动设备上安装 atx-agent(守护进程),随后 atx-agent 启动 uiautomator2 服务(默认 7912 端口)进行监听;
在 PC 上编写测试脚本并执行(相当于发送 HTTP 请求到移动设备的 server 端);
移动设备通过 WIFI 或 USB 接收到 PC 上发来的 HTTP 请求,执行制定的操作;

三、安装与启动

3.1 安装 uiautomator2
使用 pip 安装

pip install -U uiautomator2
安装完成后,使用如下 python 代码查看环境是事配置成功

说明:后文中所有代码都需要导入 uiautomator2 库,为了简化我使用 u2 代替,d 代表 driver
import uiautomator2 as u2# 连接并启动d = u2.connect() print(d.info)
能正确打印出设备的信息则表示安装成功

注意:需要安装 adb 工具,并配置到系统环境变量,才能操作手机。
安装有问题可以到 issue 列表查询:

https://github.com/openatx/uiautomator2/wiki/Common-issues
3.2 安装 weditor
weditor 是一款基于浏览器的 UI 查看器,用来帮助我们查看 UI 元素定位。

因为 uiautomator 是独占资源,所以当 atx 运行的时候 uiautomatorviewer 是不能用的,为了减少 atx 频繁的启停,就需要用到此工具

使用 pip 安装

1
pip install -U weditor
  查看安装是否成功

1
weditor --help
  出现如下信息表示安装成功

运行 weditor

1
python -m weditor#或者直接在命令行运行weditor

四、元素定位

4.1 使用方法
1
d(定位方式 = 定位值)#例:element = d(text=‘Phone’)#这里返回的是一个列表,当没找到元素时,不会报错,只会返回一个长度为 0 的列表#当找到多个元素时,会返回多个元素的列表,需要加下标再定位element[0].click()#获取元素个数print(element.count)

4.2 支持的定位方式
ui2 支持 android 中 UiSelector 类中的所有定位方式,详细可以在这个网址查看 developer.android.com/reference/a…

整体内容如下 , 所有的属性可以通过 weditor 查看到

名称描述

texttext 是指定文本的元素textContainstext 中包含有指定文本的元素textMatchestext 符合指定正则的元素textStartsWithtext 以指定文本开头的元素classNameclassName 是指定类名的元素classNameMatchesclassName 类名符合指定正则的元素descriptiondescription 是指定文本的元素descriptionContainsdescription 中包含有指定文本的元素descriptionMatchesdescription 符合指定正则的元素descriptionStartsWithdescription 以指定文本开头的元素checkable可检查的元素,参数为 True,Falsechecked已选中的元素,通常用于复选框,参数为 True,Falseclickable可点击的元素,参数为 True,FalselongClickable可长按的元素,参数为 True,Falsescrollable可滚动的元素,参数为 True,Falseenabled已激活的元素,参数为 True,Falsefocusable可聚焦的元素,参数为 True,Falsefocused获得了焦点的元素,参数为 True,Falseselected当前选中的元素,参数为 True,FalsepackageNamepackageName 为指定包名的元素packageNameMatchespackageName 为符合正则的元素resourceIdresourceId 为指定内容的元素resourceIdMatchesresourceId 为符合指定正则的元素

4.3 子元素和兄弟定位
元素定位

child()

1
#查找类名为 android.widget.ListView 下的 Bluetooth 元素d(className=“android.widget.ListView”).child(text=“Bluetooth”)# 下面这两种方式定位有点不准确,不建议使用d(className=“android.widget.ListView”).child_by_text(“Bluetooth”,allow_scroll_search=True)d(className=“android.widget.ListView”).child_by_description(“Bluetooth”)

兄弟元素定位
sibling()

1
#查找与 google 同一级别,类名为 android.widget.ImageView 的元素d(text=“Google”).sibling(className=“android.widget.ImageView”)

链式调用
1
d(className=“android.widget.ListView”, resourceId=“android:id/list”) \ .child_by_text(“Wi‑Fi”, className=“android.widget.LinearLayout”) \ .child(className=“android.widget.Switch”) \ .click()

4.4 相对定位
相对定位支持在left, right, top, bottom, 即在某个元素的前后左右

1
d(A).left(B),# 选择 A 左边的 Bd(A).right(B),# 选择 A 右边的 Bd(A).up(B), #选择 A 上边的 Bd(A).down(B),# 选择 A 下边的 B#选择 WIFI 右边的开关按钮d(text=‘Wi‑Fi’).right(resourceId=‘android:id/widget_frame’)

4.5 元素常用 API
表格标注有 @property 装饰的类属性方法,均为下方示例方式

1
d(test=“Settings”).exists

方法

描述返回值备注

exists()判断元素是否存在True,Flase@propertyinfo()返回元素的所有信息字典@propertyget_text()返回元素文本字符串

set_text(text)设置元素文本None

clear_text()清空元素文本None

center()返回元素的中心点位置(x,y)基于整个屏幕的点

exists 其它使用方法:

1
d.exists(text=‘Wi‑Fi’,timeout=5)
  info() 输出信息:

1
{ “bounds”: { “bottom”: 407, “left”: 216, “right”: 323, “top”: 342 }, “childCount”: 0, “className”: “android.widget.TextView”, “contentDescription”: null, “packageName”: “com.android.settings”, “resourceName”: “android:id/title”, “text”: “Wi‑Fi”, “visibleBounds”: { “bottom”: 407, “left”: 216, “right”: 323, “top”: 342 }, “checkable”: false, “checked”: false, “clickable”: false, “enabled”: true, “focusable”: false, “focused”: false, “longClickable”: false, “scrollable”: false, “selected”: false}

可以通过上方信息分别获取元素的所有属性

4.6 XPATH 定位
因为 Java uiautoamtor 中默认是不支持 xpath,这是属于 ui2 的扩展功能,速度会相比其它定位方式慢一些

在 xpath 定位中,ui2 中的 description 定位需要替换为 content-desc,resourceId 需要替换为 resource-id

使用方法

1

只会返回一个元素,如果找不到元素,则会报 XPathElementNotFoundError 错误# 如果找到多个元素,默认会返回第 0 个d.xpath(’//[@resource-id=“com.android.launcher3:id/icon”]’)# 如果返回的元素有多个,需要使用 all() 方法返回列表# 使用 all 方法,当未找到元素时,不会报错,会返回一个空列表d.xpath(’//[@resource-id=“com.android.launcher3:id/icon”]’).all()

五、设备交互

5.1 单击
1
d(text=‘Settings’).click()#单击直到元素消失 , 超时时间 10,点击间隔 1d(text=‘Settings’).click_gone(maxretry=10, interval=1.0)

5.2 长按
1
d(text=‘Settings’).long_click()

5.3 拖动
Android<4.3 时不能使用拖动

1

在 0.25S 内将 Setting 拖动至 Clock 上,拖动元素的中心位置# duration 默认为 0.5, 实际拖动的时间会比设置的要高d(text=“Settings”).drag_to(text=“Clock”, duration=0.25)# 拖动 settings 到屏幕的某个点上d(text=“Settings”).drag_to(877,733, duration=0.25)#两个点之间的拖动 , 从点 1 拖动至点 2d.drag(x1,y1,x2,y2)

5.4 滑动
滑动有两个,一个是在 driver 上操作,一个是在元素上操作

元素上操作
从元素的中心向元素边缘滑动

1

在 Setings 上向上滑动。steps 默认为 10# 1 步约为 5 毫秒,因此 20 步约为 0.1 sd(text=“Settings”).swipe(“up”, steps=20)

driver 上操作
即对整个屏幕操作

1

实现下滑操作x,y = d.window_size()x1 = x / 2y1 = y * 0.1y2 = y * 0.9d.swipe(x1,y1,x1,y2)

driver 滑动的扩展方法,可以直接实现滑动,不需要再自己封装定位点

1

支持前后左右的滑动# “left”, “right”, “up”, “down”# 下滑操作d.swipe_ext(“down”)

5.5 双指操作
android>4.3

对元素操作
1
d(text=‘Settings’).gesture(start1,start2,end1,end2,)# 放大操作d(text=‘Settings’).gesture((525,960),(613,1121),(135,622),(882,1540))
  封装好的放大缩小操作

1

缩小d(text=“Settings”).pinch_in()# 放大d(text=“Settings”).pinch_out()

5.6 等待元素出现或者消失
1

等待元素出现d(text=“Settings”).wait(timeout=3.0)# 等待元素消失,返回 True False,timout 默认为全局设置的等待时间d(text=‘Settings’).wait_gone(timeout=20)

5.7 滚动界面
设置 scrollable 属性为 True;

滚动类型:horiz 为水平,vert 为垂直;

滚动方向:

forward 向前
backward 向后
toBeginning 滚动至开始
toEnd 滚动至最后
to 滚动直接某个元素出现
所有方法均返回 Bool 值;

1
2
3

垂直滚动到页面顶部 / 横向滚动到最左侧d(scrollable=True).scroll.toBeginning()d(scrollable=True).scroll.horiz.toBeginning()# 垂直滚动到页面最底部 / 横向滚动到最右侧d(scrollable=True).scroll.toEnd()d(scrollable=True).scroll.horiz.toEnd()# 垂直向后滚动到指定位置 / 横向向右滚动到指定位置d(scrollable=True).scroll.to(description=" 指定位置 “)d(scrollable=True).scroll.horiz.to(description=” 指定位置 “)# 垂直向前滚动(横向同理)d(scrollable=True).scroll.forward()# 垂直向前滚动到指定位置(横向同理)d(scrollable=True).scroll.forward.to(description=” 指定位置 ")# 滚动直到 System 元素出现d(scrollable=True).scroll.to(text=“System”)

Take screenshot of widgetim = d(text=“Settings”).screenshot()im.save(“settings.jpg”)

5.8 输入
5.8.1 输入自定义文本
1

使用 adb 广播的方式输入d.send_keys(‘hello’)# 清空输入框d.clear_text()

5.8.2 输入按键
两种方法

1

发送回车d.press(‘enter’)# 第二种d.keyevent(‘enter’)

目前 press 支持的按键如下

1
“”" press key via name or key code. Supported key name includes: home, back, left, right, up, down, center, menu, search, enter, delete(or del), recent(recent apps), volume_up, volume_down, volume_mute, camera, power. “”"

keyevent 是通过 “adb shell input keyevent” 方式输入,支持按键更加丰富

更多详细的按键信息 developer.android.com/reference/a…

5.8.3 输入法切换
1

切换成 ui2 的输入法,这里会隐藏掉系统原本的输入法 , 默认是使用系统输入法# 当传入 False 时会使用系统默认输入法,默认为 Fasled.set_fastinput_ime(True)# 查看当前输入法d.current_ime()#返回值(‘com.github.uiautomator/.FastInputIME’, True)

5.8.4 模拟输入法功能
可以模拟的功能有 go ,search ,send ,next, done ,previous。

如果使用 press 输入按键无效,可以尝试使用此方法输入

1

搜索功能d.send_action(“search”)

5.9 toast 操作
1

获取 toast, 当没有找到 toast 消息时,返回 default 内容d.toast.get_message(timout=5,default=‘no toast’)# 清空 toast 缓存d.toast.reset()

5.10 监控界面
使用 wather 进行界面的监控,可以用来实现跳过测试过程中的弹框

当启动 wather 时,会新建一个线程进行监控

可以添加多个 watcher

用法
1

注册监控 , 当界面内出现有 allow 字样时,点击 allowd.watcher.when(‘allow’).click()# 移除 allow 的监控d.watcher.remove(“allow”)# 移除所有的监控d.watcher.remove()# 开始后台监控d.watcher.start()d.watcher.start(2.0) # 默认监控间隔 2.0s# 强制运行所有监控d.watcher.run()# 停止监控d.watcher.stop()# 停止并移除所有的监控,常用于初始化d.watcher.reset()

2.11.0 版本新增了一个 watch_context 方法 , 写法相比 watcher 更简洁,官方推荐使用此方法来实现监控,目前只支持 click() 这一种方法。

1
wct = d.watch_context()# 监控 ALLOWwct.when(“ALLOW”).click()# 监控 OKwct.when(‘OK’).click() # 开启弹窗监控,并等待界面稳定(两个弹窗检查周期内没有弹窗代表稳定)wct.wait_stable()#其它实现代码# 停止监控wct.stop()

5.11 多点滑动
这里可以用来实现图案解锁

使用 touch 类

1

模拟按下不放手touch.down(x,y)# 停住 3Stouch.sleep(x,y)# 模拟移动touch.move(x,y)# 模拟放开touch.up(x,y)#实现长按 , 同一个点按下休眠 5S 后抬起d.touch.down(252,1151).sleep(5).up(252,1151)# 实现四点的图案解锁,目前只支持坐标点d.touch.down(252,1151).move(559,1431).move(804,1674).move(558,1666).up(558,1666)

六、图像操作

6.1 截图
1
d.screenshot(‘test.png’)

6.2 录制视频
这个感觉是比较有用的一个功能,可以在测试用例开始时录制,结束时停止录制,然后如果测试 fail。则上传到测试报告,完美复原操作现场,具体原理后面再去研究。

首先需要下载依赖,官方推荐使用镜像下载:

1
pip3 install -U “uiautomator2[image]” -i https://pypi.doubanio.com/simple
  执行录制:

1

启动录制,默认帧率为 20d.screenrecord(‘test.mp4’)# 其它操作time.sleep(10)#停止录制,只有停止录制了才能看到视频 d.screenrecord.stop()

6.3 图片识别点击
下载与录制视频同一套依赖。

这个功能是首先手动截取需要点击目标的图片,然后 ui2 在界面中去匹配这个图片,目前我尝试了精确试不是很高,误点率非常高,不建议使用。

1

点击 d.image.click(‘test.png’)# 匹配图片,返回相似度和坐标# {‘similarity’: 0.9314796328544617, ‘point’: [99, 630]}d.image.match(‘test.png’)

七、应用管理

7.1 获取当前界面的 APP 信息
1
d.app_current()#返回当前界面的包名,activity 及 pid{ “package”: “com.xueqiu.android”, “activity”: “.common.MainActivity”, “pid”: 23007}

7.2 安装应用
可以从本地路径及 url 下载安装 APP,此方法无返回值,当安装失败时,会抛出 RuntimeError 异常

1

本地路径安装 d.app_install(‘test.apk’)# url 安装 d.app_install(‘http://s.toutiao.com/UsMYE/’)

7.3 运行应用
默认当应用在运行状态执行 start 时不会关闭应用,而是继续保持当前界面。

如果需要消除前面的启动状态,则需要加 stop=True 参数。

1

通过包名启动d.app_start(“com.xueqiu.android”,stop=True)#源码说明 def app_start(self, package_name: str, activity: Optional[str]=None, wait: bool = False, stop: bool=False, use_monkey: bool=False): “”" Launch application Args: package_name (str): package name activity (str): app activity stop (bool): Stop app before starting the activity. (require activity) use_monkey (bool): use monkey command to start app when activity is not given wait (bool): wait until app started. default False “”"

7.4 停止应用
stop 和 clear 的区别是结束应用使用的命令不同

stop 使用的是 “am force-stop”

clear 使用的是 “pm clear”

1

通过包名结束单个应用d.app_stop(“com.xueqiu.android”)d.app_clear(‘com.xueqiu.android’)# 结束所有应用 , 除了 excludes 参数列表中的应用包名# 如果不传参,则会只保留两个依赖服务应用# 会返回一个结束应用的包名列表d.app_stop_all(excludes=[‘com.xueqiu.android’])

7.5 获取应用信息

1
d.app_info(‘com.xueqiu.android’)#输出{ “packageName”: “com.xueqiu.android”, “mainActivity”: “com.xueqiu.android.common.splash.SplashActivity”, “label”: " 雪球 ", “versionName”: “12.6.1”, “versionCode”: 257, “size”: 72597243}

7.6 获取应用图标
1
img = d.app_icon(‘com.xueqiu.android’)img.save(‘icon.png’)

7.7 等待应用启动
1

等待此应用变为当前应用,返回 pid,超时未启动成功则返回 0# front 为 true 表示等待 app 成为当前 app,# 默认为 false,表示只要后台有这个应用的进程就会返回 PIDd.app_wait(‘com.xueqiu.android’,60,front=True)

7.8 卸载应用
1

卸载成功返回 true, 没有此包或者卸载失败返回 Falsed.app_uninstall(‘com.xueqiu.android’)# 卸载所有自己安装的第三方应用 , 返回卸载 app 的包名列表# excludes 表示不卸载的列表# verbose 为 true 则会打印卸载信息d.app_uninstall_all(excludes=[],verbose=True)

卸载全部应用返回的包名列表并一定是卸载成功了,最好使用 verbose=true 打印一下信息,这样可以查看到是否卸载成功

1
uninstalling com.xueqiu.android OKuninstalling com.android.cts.verifier FAIL
  或者可以修改一下源码,使其只输出成功的包名,注释的为增加的代码,未注释的是源码

1
def app_uninstall_all(self, excludes=[], verbose=False): “”" Uninstall all apps “”" our_apps = [‘com.github.uiautomator’, ‘com.github.uiautomator.test’] output, _ = self.shell([‘pm’, ‘list’, ‘packages’, ‘-3’]) pkgs = re.findall(r’package:([^\s]+)’, output) pkgs = set(pkgs).difference(our_apps + excludes) pkgs = list(pkgs) # 增加一个卸载成功的列表 #sucess_list = [] for pkg_name in pkgs: if verbose: print(“uninstalling”, pkg_name, " “, end=”", flush=True) ok = self.app_uninstall(pkg_name) if verbose: print(“OK” if ok else “FAIL”) # 增加如下语句,当成功则将包名加入 list #if ok: # sucess_list.append(pkg_name) # 返回成功的列表 # return sucess_list return pkgs

八、其它实用方法

8.1 连接设备
1
#当 PC 只连接了一个设备时,可以使用此种方式d = u2.connect()#返回的是 Device 类 , 此类继承方式如下class Device(_Device, _AppMixIn, _PluginMixIn, _InputMethodMixIn, _DeprecatedMixIn): “”" Device object “”"# for compatible with old codeSession = Device
  connect() 可以使用如下其它方式进行连接

1
#当 PC 与设备在同一网段时,可以使用 IP 地址和端口号通过 WIFI 连接,无需连接 USB 线connect(“10.0.0.1:7912”)connect(“10.0.0.1”) # use default 7912 portconnect(“http://10.0.0.1”)connect(“http://10.0.0.1:7912”)#多个设备时,使用设备号指定哪一个设备connect(“cff1123ea”) # adb device serial number

8.2 获取设备及 driver 信息
8.2.1 获取 driver 信息
1
d.info#输出{ “currentPackageName”: “com.android.systemui”, “displayHeight”: 2097, “displayRotation”: 0, “displaySizeDpX”: 360, “displaySizeDpY”: 780, “displayWidth”: 1080, “productName”: “freedom_turbo_XL”, “screenOn”: true, “sdkInt”: 29, “naturalOrientation”: true}

8.2.2 获取设备信息
会输出测试设备的所有信息,包括电池,CPU,内存等

1
d.device_info#输出{ “udid”: “61c90e6a-ba:1b:ba:46:91:0e-freedom_turbo_XL”, “version”: “10”, “serial”: “61c90e6a”, “brand”: “Schok”, “model”: “freedom turbo XL”, “hwaddr”: “ba:1b:ba:46:91:0e”, “port”: 7912, “sdk”: 29, “agentVersion”: “0.9.4”, “display”: { “width”: 1080, “height”: 2340 }, “battery”: { “acPowered”: false, “usbPowered”: true, “wirelessPowered”: false, “status”: 2, “health”: 2, “present”: true, “level”: 98, “scale”: 100, “voltage”: 4400, “temperature”: 292, “technology”: “Li-ion” }, “memory”: { “total”: 5795832, “around”: “6 GB” }, “cpu”: { “cores”: 8, “hardware”: “Qualcomm Technologies, Inc SDM665” }, “arch”: “”, “owner”: null, “presenceChangedAt”: “0001-01-01T00:00:00Z”, “usingBeganAt”: “0001-01-01T00:00:00Z”, “product”: null, “provider”: null}

8.2.3 获取屏幕分辨率
1

返回(宽,高)元组d.window_size()# 例 分辨率为 1080*1920# 手机竖屏状态返回 (1080,1920)# 横屏状态返回 (1920,1080)

8.2.4 获取 IP 地址
1

返回(宽,高)元组d.window_size()# 例 分辨率为 1080*1920# 手机竖屏状态返回 (1080,1920)# 横屏状态返回 (1920,1080)

8.2.4 获取 IP 地址
1

返回 ip 地址字符串,如果没有则返回 Noned.wlan_ip

8.3 driver 全局设置
8.3.1 使用 settings 设置
查看 settings 默认设置

1
d.settings#输出{ #点击后的延迟,(0,3)表示元素点击前等待 0 秒,点击后等待 3S 再执行后续操作 ‘operation_delay’: (0, 3), # opretion_delay 生效的方法,默认为 click 和 swipe # 可以增加 press,send_keys,long_click 等方式 ‘operation_delay_methods’: [‘click’, ‘swipe’], # 默认等待时间,相当于 appium 的隐式等待 ‘wait_timeout’: 20.0, # xpath 日志 ‘xpath_debug’: False}
  修改默认设置,只需要修改 settings 字典即可

1
#修改延迟为操作前延迟 2S 操作后延迟 4.5Sd.settings[‘operation_delay’] = (2,4.5)#修改延迟生效方法d.settings[‘operation_delay_methods’] = {‘click’,‘press’,‘send_keys’}# 修改默认等待d.settings[‘wait_timeout’] = 10

8.3.2 使用方法或者属性设置
http 默认请求超时时间

默认值 60s, d.HTTP_TIMEOUT = 60
当设备掉线时,等待设备在线时长

仅当 TMQ=true 时有效,支持通过环境变量 WAIT_FOR_DEVICE_TIMEOUT 设置d.WAIT_FOR_DEVICE_TIMEOUT = 70

元素查找默认等待时间

打不到元素时,等待 10 后再报异常d.implicitly_wait(10.0)
打开 HTTP debug 信息

d.debug = Trued.info#输出15:52:04.736 $ curl -X POST -d ‘{“jsonrpc”: “2.0”, “id”: “0eed6e063989e5844feba578399e6ff8”, “method”: “deviceInfo”, “params”: {}}’ 'http://localhost:51046/jsonrpc/0’15:52:04.816 Response (79 ms) >>>{“jsonrpc”:“2.0”,“id”:“0eed6e063989e5844feba578399e6ff8”,“result”:{“currentPackageName”:“com.android.systemui”,“displayHeight”:2097,“displayRotation”:0,“displaySizeDpX”:360,“displaySizeDpY”:780,“displayWidth”:1080,“productName”:“freedom_turbo_XL”,“screenOn”:true,“sdkInt”:29,“naturalOrientation”:true}}<<< END

休眠

相当于 time.sleep(10)d.sleep(10)
8.4 亮灭屏
1

亮屏d.screen_on()# 灭屏d.screen_off()

8.5 屏幕方向
1

设置屏幕方向d.set_orientation(value)# 获取当前屏幕方向d.orientation

value 值参考,只要是元组中的任一一个值就可以。

1

正常竖屏(0, “natural”, “n”, 0), # 往左横屏,相当于手机屏幕顺时针旋转 90 度# 现实中如果要达到此效果,需要将手机逆时针旋转 90 度 (1, “left”, “l”, 90),# 倒置,这个需要看手机系统是否支持 , 倒过来显示 (2, “upsidedown”, “u”, 180), # 往右横屏,调整与往左相反,屏幕顺时针旋转 270 度 (3, “right”, “r”, 270))

8.6 打开通知栏与快速设置
打开通知栏

1
d.open_notification()
  打开快速设置

1
d.open_quick_settings()

8.7 文件导入导出
8.7.1 导入文件
1

如果是目录,这里 “/sdcrad/” 最后一个斜杠一定要加,否则会报错d.push(“test.txt”,"/sdcrad/")d.push(“test.txt”,"/sdcrad/test.txt")

8.7.2 导出文件
1
d.pull(’/sdcard/test.txt’,‘text.txt’)

8.8 执行 shell 命令
使用 shell 方法执行

8.8.1 执行非阻塞命令
output 返回的是一个整体的字符串,如果需要抽取值,需要对 output 进行解析提取处理

1

返回输出和退出码,正常为 0,异常为 1output,exit_code = d.shell([“ls”,"-l"],timeout=60)

8.8.2 执行阻塞命令(持续执行的命令)
1

返回一个命令的数据流 output 为 requests.models.Responseoutput = d.shell(‘logcat’,stream=True)try: # 按行读取,iter_lines 为迭代响应数据,一次一行 for line in output.iter_lines(): print(line.decode(‘utf8’))finally: output.close()

源码描述

1
def shell(self, cmdargs: Union[str, List[str]], stream=False, timeout=60): “”" Run adb shell command with arguments and return its output. Require atx-agent >=0.3.3 Args: cmdargs: str or list, example: “ls -l” or [“ls”, “-l”] timeout: seconds of command run, works on when stream is False stream: bool used for long running process. Returns: (output, exit_code) when stream is False requests.Response when stream is True, you have to close it after using Raises: RuntimeError For atx-agent is not support return exit code now. When command got something wrong, exit_code is always 1, otherwise exit_code is always 0 “”"

8.9 session(目前已经被弃用)
8.10 停止 UI2 服务
因为有 atx-agent 的存在,Uiautomator 会被一直守护着,如果退出了就会被重新启动起来。但是 Uiautomator 又是霸道的,一旦它在运行,手机上的辅助功能、电脑上的 uiautomatorviewer 就都不能用了,除非关掉该框架本身的 uiautomator

使用代码停止
1
d.service(“uiautomator”).stop()

手动停止
直接打开 ATX APP(init 成功后,就会安装上),点击关闭 UIAutomator

以上,欢迎大家一起交流探讨。

最后:


欢迎关注公众号:程序员阿沐,领取一份300页pdf文档的Python自动化测试工程师核心知识点总结!软件测试技术交流群:(810119819) 这些资料的内容都是面试时面试官必问的知识点,篇章包括了很多知识点,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

干货|一文搞定 uiautomator2 自动化测试工具使用相关推荐

  1. 一文搞定JVM常见工具和优化策略

    目录 1. 概述篇 1.1. 背景说明 1.3. 调优概述 1.4. 性能优化的步骤 2. JVM 监控及诊断工具-命令行篇 2.1. 概述 2.2. jps:查看正在运行的 Java 进程 2.3. ...

  2. | 一文 uiautomator2 自动化测试工具使用

    在上一篇文章中分享了 pytest 的基本用法,本文进一步介绍 pytest 的其他实用特性和进阶技巧. pytest fixtures pytest 中可以使用 @pytest.fixture 装饰 ...

  3. php带参数单元测试_一文搞定单元测试核心概念

    基础概念 单元测试(unittesting),是指对软件中的最小可测试单元进行检查和验证,这里的最小可测试单元通常是指函数或者类.单元测试是即所谓的白盒测试,一般由开发人员负责测试,因为开发人员知道被 ...

  4. 一文搞定Qt读写excel以及qt读写xml数据

    一文搞定Qt读写excel以及qt读写xml数据 最终的实现效果图 RC_ICONS = logo.ico .pro文件同级目录下加入 logo.ico 图标文件,运行文件,文件的图标就被写入软件 u ...

  5. 一文搞定B站弹幕生成云图

    一文搞定B站弹幕生成云图 最近学了词云图, 感觉非常有趣,我们做一些图试试, 最近我们遭受了新冠疫情 , 关于这个点,去看看b站弹幕都在说什么? B站的弹幕接口 直接从B站某视频源中找半天没找到弹幕的 ...

  6. 【语义分割】综述——一文搞定语义分割

    本文记录了博主阅读的关于语义分割(Semantic Segmentation)的综述类文章的笔记,更新于2019.02.19. [语义分割]综述--一文搞定语义分割 参考文献网址 An overvie ...

  7. 一文搞定Python中的时间转化

    一文搞定Python中的时间转化 在生活和工作中,我们每个人每天都在和时间打交道: 早上什么时候起床? 地铁几分钟来一趟? 中午什么时候开始午休? 明天是星期几? 距离上次买衣服已经2个月呢? 领导让 ...

  8. 【嵌入式开发-AD19】六文搞定Altium Designer-第一章:AD介绍及原理图库的创建

    [嵌入式开发-AD19]六文搞定Altium Designer-第一章:AD介绍及原理图库的创建 在文章的开头我想首先简单介绍一下国产全免费EDA软件,嘉立创EDA.嘉立创EDA拥有网页版和安装版两种 ...

  9. 一文搞定MAVLINK软件协议

    转载:https://mp.weixin.qq.com/s/iGURlSS7V-5iBCEtgpzT7w 一文搞定MAVLINK软件协议 原创 L君 TBUS社区 2019-11-06 本文纯属资深程 ...

最新文章

  1. html表单颜色选择器,如何在Django管理中使用HTML5颜色选择器
  2. (一)TestNG测试框架之HelloWorld入门
  3. python怎么导入时间-python模块导入和time模块
  4. TypeScript方法的定义
  5. Redis源码编译安装
  6. HTML页面背景音乐控制
  7. 让Tee 7.x版本和FastReport 3.x版本共存
  8. STL stack 容器
  9. python经典书籍推荐:Python核心编程
  10. 你现在可以使用的10个JavaScript代码段
  11. 查看Linux连接数
  12. Android 编程下 ListView 的 HeaderView 和 FooterView 不可选择点击
  13. 陈小琼,你真不好等!
  14. 字符串函数---atof()函数详解及实现
  15. 万能平板刷机软件_平板电脑怎么刷机 平板电脑刷机方法【教程】
  16. 计算机网络技术试题 中职,计算机网络技术试题(附答案)中等职业学校.doc
  17. vp9 segment 详细分析
  18. 昆山花桥房都要上万 上海买家占9成“江山”
  19. 各向异性渲染(一)基础理论
  20. 数据中台,我还能爱你吗(文末送书)

热门文章

  1. 《计算机网络--自顶向下方法》第三章--运输层
  2. python的分布式爬虫框架
  3. FFmpeg进阶: 截取视频生成gif动图
  4. 禹司凤扛鸿蒙炉,琉璃美人煞12人物结局,司凤 璇玑完满,柏麟 副宫主结局大快人心...
  5. 我的世界java1.16.3村庄种子,我的世界2020年最新版村庄种子
  6. vue使用html2canvas截取div内容生成海报~ vue生成海报
  7. 2021年一战南大AI上岸经验贴
  8. 2 c++编程-核心
  9. 和平精英分数计算机制,和平精英段位对应的积分完整一览 和平精英多少分上王牌...
  10. C盘各个文件的简单介绍