1.说明
我们之前在电脑操作手机进行自动化测试,基本上都是通过Appium的,这个工具确实强大,搭配谷歌官方的UiAutomator基本上可以完成各种测试,但缺点也很明显,配置环境太麻烦了,需要jdk、sdk等,后来有人在UiAutomator的基础上使用http请求rpc服务的方式做了一个uiautomator,但这个项目很久没有维护了,后面有人在此基础上进行重构和精简,所以又有了uiautomator2,这是目前为止比较好用的Python操作手机的测试库之一
2.安装环境
2.1 安装uiautomator2
使用uiautomator2的要求是,Android版本 4.4或以上版本,Python 3.6或以上版本,社区反馈3.8.0不支持, 但是3.8.2支持
pip install uiautomator2
2.2 安装weditor
uiautomator2是控制手机的, 还需要一个查看手机元素的库,与uiautomator2配套的是weditor。
注意,如果你安装0.6.5或以上版本的weditor可能会遇到编码错误
UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0xad in position 825: illegal multibyte sequence。所以,你可以选择0.6.4版本
pip install weditor==0.6.4
或者可以把Python环境的默认编码改成utf-8(在cmd执行)
set PYTHONUTF8=1
2.3 安装adb
adb的作用是连接安卓设备,比如说查看当前已连接上电脑的设备,我们可以在android官网下载platform-tools,里面包含了adb程序,下载之后无需安装,进行解压就能用,要是觉得不方便使用的话你可以把adb.exe添加到系统环境变量
https://developer.android.com/studio/releases/platform-tools
3.初始化
首先使用adb查看你的手机是否已连接上电脑(需要打开开发者选项),会显示设备序列号
adb.exe devices
之后初始化uiautomator2 ,它会在你的手机上安装一个叫做atx-agent的应用
uiautomator2 init
我这里使用的是模拟器,大概结果如下图,初始化成功的话后面会有Successfully init AdbDevice(serial=XXX)
的提示
4.uiautomator2连接设备
通过USB连接
import uiautomator2 as u2
d = u2.connect('emulator-5554')
print(d.info)
通过WiFi连接(确保手机与电脑处于同一个局域网并且能ping同手机)
import uiautomator2 as u2
d = u2.connect('192.168.0.100')
通过adbWiFi连接
import uiautomator2 as u2
d = u2.connect_adb_wifi("192.168.0.101:5555")
5.weditor定位元素
现在已经可以通过uiautomator2操作手机了,如果想要定位元素的话,刚刚安装好的weditor就可以派上用场了,在cmd输入weditor,它会自动打开浏览器
weditor
我们在打开的网页上面输入设备号或者ip,点击“Connect”,如果“Connect”按钮出现一个绿色的树叶图标说明已经连接上。再往右一点有一个“Dump hierarchy”按钮,点击一下就刷新页面了,你也可以打开“实时”选项,这样它会一直刷新页面。下面的窗口会显示当前手机的页面,点击一下某个控件就能显示它的信息,右边还有一个写测试代码的窗口,虽然我们一般都是通过Python控制,但Python端每次运行都需要花费时间,可以先在网页端测试一下定位是否准确
6.操作APP
6.1 查看APP包名
对于操作APP,基本上都是通过包名和activity控制的,比如说打开关闭、安装卸载等操作,所以获取包名是第一步,你可以通过weditor查看某个应用的包名,也可以通过uiautomator2获取
import uiautomator2 as u2
d = u2.connect("emulator-5554")
all_pkg_list = d.app_list()
print(len(all_pkg_list), all_pkg_list)
running_pkg_list = d.app_list_running()
print(len(running_pkg_list), running_pkg_list)
6.2 获取包信息
info = d.app_info("com.android.settings")
print(info)
6.3 启动停止APP
d.app_start("com.android.settings", wait=True)
pid = d.app_wait("com.android.settings", timeout=20)
print(pid)
d.app_stop("com.android.settings")
d.app_stop_all(excludes=["com.android.browser", "com.android.bluetooth"])
6.4 安装卸载APP
d.app_install(r"C:\Users\admin\Desktop\IcyFtpServer_v1.0.apk")
d.app_uninstall("com.ice.icyftpserver")
7.操作元素
当打开APP之后,我们就可以操作APP了,包括定位元素,点击、长按等操作
7.1 选择器
定位元素很重要,只有获取到某个元素才可以操作。定位元素的方式有多种,比如说index、resourceId、className、text、textContains等,其中比较准确的是通过index和resourceId进行定位(速度很快),index、resourceId和className的值都可以在weditor看到。text是指元素的全部文本,textContains是指包含部分文本,textStartsWith是指以某文本开始,这几种选择器都会搜索页面,所以速度会比较慢
d(index=1).click()
d(resourceId="com.android.settings:id/search_action_bar_title").click()
d(className="android.widget.TextView").click()
d(text="在设置中搜索").click()
d(textContains="在设置中搜索").click()
d(textStartsWith="在设置中").click()
我们注意到,除了index和resourceId可以准确定位到唯一的元素,其他选择器都可能定位到多个元素,为了提高准确率,我们可以同时使用多个选择器
d(className="android.widget.TextView", textContains="在设置中").click()
谷歌官网给出的选择还有很多,感兴趣的话可以看一下
https://developer.android.com/reference/android/support/test/uiautomator/UiSelector
选择选择器返回的是一个UiObject对象,但要注意,即使没有定位到,它也不会报错,直到你进行点击等操作的时候才会(超时)报错
UiObject可以同时包含多个元素,即它是一个容器,因此,我们可以查看该对象包含多少个元素,并且可以通过遍历的方式依次访问选中的元素
selected_el = d(resourceId="com.android.settings:id/search_action_bar_title")
print(selected_el.count)
print(len(selected_el))
print(selected_el[0])
for el in selected_el:
print(el)
selected_el = d(className="android.widget.TextView", instance=0)
print(len(selected_el))
另外,也可以通过当前选择的元素选择子元素或者兄弟元素,目前不支持选择父元素
d(className="android.widget.LinearLayout").child(text="网络和互联网")
d(className="android.widget.LinearLayout").sibling(className="android.widget.ImageView")
d(resourceId="android:id/title").up()
d(resourceId="android:id/title").down()
d(resourceId="android:id/title").left()
d(resourceId="android:id/title").right()
7.2 元素信息
通过选择器定位元素之后,可以调用一下exists属性看是否真的存在,如果存在再进行其他操作,如果想知道元素的边界、中心点的位置也是可以获取到的
selected_el = d(resourceId="com.android.settings:id/search_action_bar_title")
if selected_el.exists:
print("info:", selected_el.info)
print(selected_el.bounds())
print(selected_el.center())
print(selected_el.count)
7.3 点击和长按
一般来说,获取到元素之后再进行点击或者长按操作
selected_el = d(resourceId="com.android.settings:id/search_action_bar_title")
selected_el.click(timeout=None, offset=None)
selected_el.long_click(duration=0.5, timeout=None)
selected_el.click_gone(maxretry=10, interval=1.0)
如果你不想通过定位到的元素进行点击,而是通过指定的x、y位置,也是可以的
x, y = 384.0, 191.5
d.click(x, y)
d.long_click(x, y, duration=0.5)
d.double_click(x, y, duration=0.1)
7.4 设置和清除文本
除了点击,还经常需要在文本框输入文本或者清除文本,或者获取文本的内容
d.app_start("com.android.settings", wait=True)
d(resourceId="com.android.settings:id/search_action_bar_title").click()
selected_el = d(resourceId="android:id/search_src_text")
selected_el.set_text("wifi")
print(selected_el.get_text())
selected_el.clear_text()
print(selected_el.get_text())
7.5 滑动操作
可以调用swipe()方法进行滑动,支持上下左右四个方向,它需要两个参数,direction是方向,支持up、down、left、right,steps是长度,默认值是10(一步大概5毫秒)
d(resourceId="com.android.settings:id/main_content").swipe(direction="up", steps=10)
基于时间的滚动感觉不是很好控制,所以还可以调用swipe_ext()方法,控制滑动比例,比如说向上滑动80%的距离
d.swipe_ext("up", scale=0.8)
8.屏幕操作
8.1 屏幕分辨率
d.info可以获取到屏幕分辨率和物理分辨率,也调用window_size()方法直接返回屏幕分辨率
print(d.info)
print(d.window_size())
8.2 截屏
如果看到一个漂亮的界面,可以选择截个图。可以调用screenshot()方法截图,第一个参数是图片保存的路径,第二个参数是处理图片的库,默认是pillow(也支持opencv),如果你没有安装pillow或opencv,那就指定第二个参数为raw
d.screenshot("./test.jpg")
import cv2
image = d.screenshot(format='opencv')
cv2.imwrite('home.jpg', image)
img = d.screenshot(format='raw')
with open("test.jpg", "wb") as f:
f.write(img)
8.3 息屏亮屏
我们可以锁屏和亮屏,可以在info信息里面找到当前是息屏还是亮屏
d.screen_on()
print(d.info.get('screenOn'))
d.screen_off()
print(d.info.get('screenOn'))
d.unlock()
8.4 自动旋转屏幕
d.set_orientation("left")
time.sleep(2)
d.set_orientation("right")
time.sleep(2)
d.set_orientation("natural")
time.sleep(2)
d.freeze_rotation(True)
9.按键事件
目前uiautomation2支持的按键还不是很多,大概有home, back, left, right, up, down, center, menu, search, enter, delete(or del), recent(recent apps), volume_up, volume_down, volume_mute, camera, power.
d.press("home")
d.press("recent")
d.press("back")
d.press("power")
d.press("menu")
d.press("volume_up")
d.press("volume_down")
d.press("volume_mute")
除了上面的press某些按键之外,也可以调出键盘,然后发送按键。不过,如果是输入框输入文本,还是建议使用set_text()更方便
d(resourceId="android:id/search_src_text").send_keys("abcdefg")
d(resourceId="android:id/search_src_text").set_text("abcdefg")
10.上传下载文件
有时候需要把电脑端的文件上传到手机端或者从手机端下载到电脑端。不过一定要注意,存储位置一定要存在并且有读写权限
d.push(r"C:\Users\admin\Desktop\test.txt", "/storage/emulated/0/")
d.pull("/storage/emulated/0/test.txt", "test2.txt")
11.执行shell命令
如果uiautomator2的操作都不能满足你的需求,你也可以直接通过adb执行原生shell命令,支持传参
output, exit_code = d.shell("pwd", timeout=60)
print(output, exit_code)
output = d.shell("pwd", stream=True)
print(output.text)
output, exit_code = d.shell(["ls", "-l"])
print(output, exit_code)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)