写点什么

软件测试 / 测试开发丨 uiautomator2 自动化测试工具使用

作者:测试人
  • 2023-04-26
    北京
  • 本文字数:10129 字

    阅读完需:约 33 分钟


获取更多相关知识

霍格沃兹测试学院是 python-uiautomator2 金牌赞助商,跟着开源项目作者学测试开发实战。

一、背景简介

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

  1. 测试脚本只能使用 Java 语言;

  2. 测试脚本要打包成 jar 或者 apk 包上传到设备上才能运行;

实际工作中,我们希望测试逻辑能够用 Python 编写,能够在电脑上运行的时候就控制手机。所以基于这个目的开发了 python-uiautomator2 自动化测试开源工具,其封装了谷歌自带的 uiautomator2 测试框架,可以运行在支持 Python 的任一系统上,目前版本为 V2.10.2。

GitHub 开源地址:

https://github.com/openatx/uiautomator2
复制代码

二、工作原理


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

  • python 端: 运行脚本,并向移动设备发送 HTTP 请求;

  • 移动设备:移动设备上运行了封装了 uiautomator2 的 HTTP 服务,解析收到的请求,并转化成 uiautomator2 的代码;

整个过程:

  1. 在移动设备上安装 atx-agent(守护进程),随后 atx-agent 启动 uiautomator2 服务(默认 7912 端口)进行监听;

  2. 在 PC 上编写测试脚本并执行(相当于发送 HTTP 请求到移动设备的 server 端);

  3. 移动设备通过 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 安装

pip install -U weditor
复制代码

查看安装是否成功

weditor --help
复制代码

出现如下信息表示安装成功


运行 weditor

python -m weditor#或者直接在命令行运行weditor
复制代码

四、元素定位

4.1 使用方法

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

4.2 支持的定位方式

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

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

4.3 子元素和兄弟定位

子元素定位

child()

#查找类名为 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()

#查找与 google 同一级别,类名为 android.widget.ImageView 的元素d(text="Google").sibling(className="android.widget.ImageView")
复制代码

链式调用

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, 即在某个元素的前后左右

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 装饰的类属性方法,均为下方示例方式

d(test="Settings").exists
复制代码


exists 其它使用方法:

d.exists(text='Wi‑Fi',timeout=5)
复制代码

info() 输出信息:

{  "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

使用方法

# 只会返回一个元素,如果找不到元素,则会报 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 单击

d(text='Settings').click()#单击直到元素消失 , 超时时间 10,点击间隔 1d(text='Settings').click_gone(maxretry=10, interval=1.0)
复制代码

5.2 长按

d(text='Settings').long_click()
复制代码

5.3 拖动

Android<4.3 时不能使用拖动

# 在 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 上操作,一个是在元素上操作

元素上操作

从元素的中心向元素边缘滑动

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

driver 上操作

即对整个屏幕操作

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

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

# 支持前后左右的滑动# "left", "right", "up", "down"# 下滑操作d.swipe_ext("down")
复制代码

5.5 双指操作

android>4.3

对元素操作

d(text='Settings').gesture(start1,start2,end1,end2,)# 放大操作d(text='Settings').gesture((525,960),(613,1121),(135,622),(882,1540))
复制代码

封装好的放大缩小操作

# 缩小d(text="Settings").pinch_in()# 放大d(text="Settings").pinch_out()
复制代码

5.6 等待元素出现或者消失

# 等待元素出现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 值;

# 垂直滚动到页面顶部 / 横向滚动到最左侧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 widget
im = d(text="Settings").screenshot()im.save("settings.jpg")
复制代码

5.8 输入

5.8.1 输入自定义文本

# 使用 adb 广播的方式输入d.send_keys('hello')# 清空输入框d.clear_text()
复制代码

5.8.2 输入按键

两种方法

# 发送回车d.press('enter')# 第二种d.keyevent('enter')
复制代码

目前 press 支持的按键如下

  """        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 输入法切换

# 切换成 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 输入按键无效,可以尝试使用此方法输入

# 搜索功能d.send_action("search")
复制代码

5.9 toast 操作

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

5.10 监控界面

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

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

可以添加多个 watcher

用法

# 注册监控 , 当界面内出现有 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() 这一种方法。

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

5.11 多点滑动

这里可以用来实现图案解锁

使用 touch 类

# 模拟按下不放手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 截图

d.screenshot('test.png')
复制代码

6.2 录制视频

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

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

pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
复制代码

执行录制:

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

6.3 图片识别点击

下载与录制视频同一套依赖。

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

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

七、应用管理

7.1 获取当前界面的 APP 信息

d.app_current()#返回当前界面的包名,activity 及 pid{    "package": "com.xueqiu.android",    "activity": ".common.MainActivity",    "pid": 23007}
复制代码

7.2 安装应用

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

# 本地路径安装 d.app_install('test.apk')# url 安装 d.app_install('http://s.toutiao.com/UsMYE/')
复制代码

7.3 运行应用

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

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

# 通过包名启动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”

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

7.5 获取应用信息

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 获取应用图标

img = d.app_icon('com.xueqiu.android')img.save('icon.png')
复制代码

7.7 等待应用启动

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

7.8 卸载应用

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

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

uninstalling com.xueqiu.android  OKuninstalling com.android.cts.verifier  FAIL
复制代码

或者可以修改一下源码,使其只输出成功的包名,注释的为增加的代码,未注释的是源码

    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 连接设备

#当 PC 只连接了一个设备时,可以使用此种方式d = u2.connect()#返回的是 Device 类 , 此类继承方式如下
class Device(_Device, _AppMixIn, _PluginMixIn, _InputMethodMixIn, _DeprecatedMixIn): """ Device object """

# for compatible with old codeSession = Device
复制代码

connect() 可以使用如下其它方式进行连接

#当 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 信息

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,内存等

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 获取屏幕分辨率

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

8.2.4 获取 IP 地址

# 返回 ip 地址字符串,如果没有则返回 Noned.wlan_ip
复制代码

8.3 driver 全局设置

8.3.1 使用 settings 设置

查看 settings 默认设置

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 字典即可

#修改延迟为操作前延迟 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 亮灭屏

# 亮屏d.screen_on()# 灭屏d.screen_off()
复制代码

8.5 屏幕方向

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

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

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

8.6 打开通知栏与快速设置

打开通知栏

d.open_notification()
复制代码

打开快速设置

d.open_quick_settings()
复制代码

8.7 文件导入导出

8.7.1 导入文件

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

8.7.2 导出文件

d.pull('/sdcard/test.txt','text.txt')
复制代码

8.8 执行 shell 命令

使用 shell 方法执行

8.8.1 执行非阻塞命令

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

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

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

# 返回一个命令的数据流 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()
复制代码

源码描述

    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

使用代码停止

d.service("uiautomator").stop()
复制代码

手动停止

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

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

发布于: 刚刚阅读数: 3
用户头像

测试人

关注

专注于软件测试开发 2022-08-29 加入

霍格沃兹测试开发学社,测试人社区:https://ceshiren.com/t/topic/22284

评论

发布
暂无评论
软件测试/测试开发丨uiautomator2 自动化测试工具使用_软件测试_测试人_InfoQ写作社区