【测试】Python手机自动化测试库uiautomator2和weditor的详细使用

news2024/11/17 11:32:18

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')  # emulator-5554就是在adb devices显示的序列号
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)

# 获取正在运行的APP的包名
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)
# 获取到的信息如下
# {'packageName': 'com.android.settings', 'mainActivity': 'Settings', 'label': 'Settings', 'versionName': '9', 'versionCode': 28, 'size': 49083357}
6.3 启动停止APP
# 直接启动APP,若wait设置为True则一直等到启动结束
d.app_start("com.android.settings", wait=True)

# 等待APP启动结束,默认20秒超时,启动后 返回pid,如果启动失败则pid为0
pid = d.app_wait("com.android.settings", timeout=20)
print(pid)

# 停止APP
d.app_stop("com.android.settings")
# 停止所有APP,excludes参数可以指定哪些APP不停止
d.app_stop_all(excludes=["com.android.browser", "com.android.bluetooth"])
6.4 安装卸载APP
# 安装APP,参数可以是本地文件也可以是url
d.app_install(r"C:\Users\admin\Desktop\IcyFtpServer_v1.0.apk")

# 卸载APP
d.app_uninstall("com.ice.icyftpserver")
# 卸载所有APP,excludes参数指定要保留的APP
# d.app_uninstall_all(excludes=["app_uninstall_all"])

7.操作元素

当打开APP之后,我们就可以操作APP了,包括定位元素,点击、长按等操作

7.1 选择器

定位元素很重要,只有获取到某个元素才可以操作。定位元素的方式有多种,比如说index、resourceId、className、text、textContains等,其中比较准确的是通过index和resourceId进行定位(速度很快),index、resourceId和className的值都可以在weditor看到。text是指元素的全部文本,textContains是指包含部分文本,textStartsWith是指以某文本开始,这几种选择器都会搜索页面,所以速度会比较慢

# 通过index定位
d(index=1).click()

# 通过资源id定位
d(resourceId="com.android.settings:id/search_action_bar_title").click()

# 通过该className定位
d(className="android.widget.TextView").click()

# 通过文本定位,速度比较慢
d(text="在设置中搜索").click()
# 包含XXX文本
d(textContains="在设置中搜索").click()
# 以XXX开始的文本
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))

另外,也可以通过当前选择的元素选择子元素或者兄弟元素,目前不支持选择父元素

# 通过child()选择子元素
d(className="android.widget.LinearLayout").child(text="网络和互联网")
# 通过sibling()选择兄弟元素
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:
    # 元素的信息,与在weditor上看到的基于一致
    print("info:", selected_el.info)
    # 元素边界,左上点和右下点的x、y
    print(selected_el.bounds())
    # 元素的中心位置
    print(selected_el.center())
    # 元素个数,即len(selected_el)
    print(selected_el.count)
7.3 点击和长按

一般来说,获取到元素之后再进行点击或者长按操作

selected_el = d(resourceId="com.android.settings:id/search_action_bar_title")
# 点击元素,timeout是等待元素出现超时时间,offset是点击位置的偏移量
selected_el.click(timeout=None, offset=None)
# 长按元素,duration长按事件,默认是0.5秒,timeout是等待元素出现的超时时间
selected_el.long_click(duration=0.5, timeout=None)
# 一直点击直到元素小时,maxretry是最大点击次数,默认是10,interval是每次点击间隔,默认是1秒
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

# 使用pillow
d.screenshot("./test.jpg")

# 使用opencv
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'))  # True
# 锁屏
d.screen_off()
print(d.info.get('screenOn'))  # False

# 解锁屏幕,实际过程是先按下power键再滑动屏幕
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()更方便

# 依次键入a、b、c、d、e、f、g键
d(resourceId="android:id/search_src_text").send_keys("abcdefg")
# 直接把输入框文本设置成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命令,支持传参

# 调用shell命令,执行成功时返回码是0
output, exit_code = d.shell("pwd", timeout=60)
print(output, exit_code)

# 当stream为True,则只返回Response对象
output = d.shell("pwd", stream=True)
print(output.text)

# 如果该命令有参数,可以使用列表的方式
output, exit_code = d.shell(["ls", "-l"])
print(output, exit_code)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用较新版本的IDEA 2022.3.2 创建Java Web的maven项目

1.创建项目 正常三步走,没什么可说的 2.用模板创建项目(重要) 第一步,一定要选Jakarta EE。这个模板是基于JavaWeb的一个标准模板,如果选了maven中的JavaWeb模板,那就变成了web目录在根目录下,…

测试跟踪模块UX交互升级,多个X-Pack功能开放至开源版,MeterSphere开源持续测试平台v2.7.0发布

2023年2月24日,MeterSphere一站式开源持续测试平台正式发布v2.7.0版本。 在这一版本中,MeterSphere在测试跟踪模块进行了UX交互升级,整个页面采用轻量化设计进行整体降噪,页面信息更加清晰易懂,操作流程更顺畅&#x…

【学习笔记】深入理解JVM之类加载机制

【学习笔记】深入理解JVM之类加载机制 以后基本上都在语雀上面更新,大家有兴趣可以看看嗷! 首发地址: 知识库 文章流程图: 1、概述 首先我们先来看看一个 Class 文件所需要经过的一个流程图: 而我们今天要重点需讲的…

如何保护阿里云、政采云等云市场三方账号安全?

什么是云市场?根据百度百科释义,云市场是指物联网中分布在不同地点的海量的商品生产者和消费者之间各种经济关系的集合体,是通过相对集中的云平台资源联合物联网各个感知节点信息资源的方式,以运行分布在不同地点的海量的经济交换…

Java——数组

目录 前言 一、数组的定义 二、数组声明和创建 三、三种初始化及内存分析 Java内存分析 三种初始化 静态初始化 动态初始化 数组的默认初始化 数组的四个基本特点 四、下标越界及小结 五、数组的使用 For-Each循环 数组作方法入参 数组作返回值 六、二维数组 七…

项目管理工具dhtmlxGantt甘特图入门教程(十四):导出/导入 Excel到 iCal

这篇文章给大家讲解利用dhtmlxgantt导入/导出Excel到iCal的操作方法。 dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表,可满足应用程序的所有需求,是完善的甘特图图表库 DhtmlxGantt正版试用下载(qun;765665…

中国跨境平台出海,产业带依然是最大优势

对外贸工厂来说,借助跨境电商服务平台开拓海外市场可行吗?2023年2月11日,在郑州荥阳举办的Starday线下招商会联合线上直播荥阳站上,这是很多现场参会的企业负责人面对大屏上的招商介绍宣传时,大脑飞速思考的问题。2023…

android kotlin 协程(六) 源码浅析

android kotlin 协程(六) 源码浅析 前言: kotlin协程源码十分庞大, 本篇只能吧我理解的源码聊一聊,不会特别深入研究,只会浅浅的看看表层. 本来计划协程系列是10篇左右,后续是flow热流冷流之类的, 冷流操作符之类的应该不会在写了, flow当作Rxjava来用就可以,后续可能还会写一…

Bitlocker加密,与解除加密

引文:应为C盘空间不够用了,想着用U盘从新给C盘分下区。操作时才发现我系统里的磁盘都是Bitlocker加密的,分区工具操作不了磁盘,所以就找到一下方法来解决。1,先讲一下解除加密:直接点击 :设置-&…

python pandas 常用方法汇总

前言 一、pandas是什么? 二、使用步骤 1.引入库 2.处理时间序列数据 3.分组聚合(groupby) 3.1基本方法 3.2具体使用:如图包含三个字段,company、salary、age 总结 Pandas 最最常用函数罗列 Pandas 函数用法示…

软件测试2年半的我,谈谈自己的理解...

软件测试两年半的我,谈谈自己的理解从2020年7月毕业,就成为一名测试仔。日子混了一鲲年,感觉需要好好梳理一下自己的职业道路了,回顾与总结下吧。一、测试的定位做事嘛,搞清楚自己的定位很重要。要搞清楚自己的定位&am…

新手小白根据Forexclub6点建议就能选择到最佳外汇经纪商

选择外汇经纪商很重要,尤其是对于外汇交易者新手而言。 在确定您计划使用的外汇交易员之前,Forexclub建议考虑以下6个因素产品丰富即使在这个阶段,您只对外汇交易感兴趣,拥有期权也是件好事。 大多数外汇经纪商提供对其他金融资产…

Excel工作表不能移动或复制?看看是不是这两个原因

Excel工作表不能移动或复制?今天来看看如何解决。 大家都知道,Excel表格分为工作簿和工作表,工作簿就是整个Excel文件;工作簿里面,也就是Excel表可以有多个工作表。 而各个工作表之间是可以相互移动或复制的&#xf…

C++赋值运算符重载

赋值运算符重载 目录赋值运算符重载示例1:示例2:示例3:示例4:很巧妙的是,在编写这篇文章时(2023年2月27日),再加100天就是6月7日,恰好是今年高考的百日誓师! …

蓝库云|什么是供应链管理?SCM对制造业的重要性

企业在产品的销售经营上,往往不会考量到供应链管理(SCM)的流程规划,但现今的商业环境与以往不同,高度竞争与客户不断提升的期望,藉由做好供应链管理(SCM),才能更准时的提供优质产品与优良服务,增强企业竞争…

HTML、CSS学习笔记4(3D转换、动画)

目录 一、空间转换(3D转换) 1.空间位移 语法: 取值:(正负均可) 透视: 2.空间旋转 3.立体呈现 二、动画(animation) 1.动画的使用 先定义动画 再调用定义好的动画 …

YSYY科研试剂DSPE-PEG-Cholesterol;磷脂聚乙二醇胆固醇简介;DSPE-PEG-胆固醇

二硬脂酰磷脂酰乙酰胺-聚乙二醇-胆固醇,DSPE-PEG-CLS,DSPE-PEG-Cholesterol 结构式: 中文名称:二硬脂酰磷脂酰乙酰胺-聚乙二醇-胆固醇英文名称:1,2-distearoyl-sn-glycero-3-phosphoethanolamine-N-[ Cholesterol(polyethylene g…

消息队列介绍和RabbitMQ的安装

1.消息队列 1.1 MQ的相关概念 1.1.1 什么是MQ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是message 而已,还是一种跨进程的通信机制,用于上下游传递消息。在…

视频营销活动中7个常见的错误

如今,越来越多的企业在社交媒体平台上开展视频营销活动。与其他传统营销策略不同,视频营销可以为企业带来更多的销售机会。随着越来越多的视频社交媒体平台的出现,营销人员更应该抓住这个机会。但在开始视频创作之前,您需要有一个…

一次查询的全过程

一次查询的全过程: 比方说我这里有一个订单系统,一条查询请求发送过来,它内部是怎么执行的呢? 用户发送请求到业务系统,就会有一条线程来处理这个请求该线程会在数据库连接池里面获取一个JDBC连接MySQL工作线程会监听数…