python --自动化测试UiAutomator2

news2024/12/28 19:34:11

安装adb

安装adb后使用命令 adb devices 出现下图即可;
在这里插入图片描述

安装python依赖(uiautomator2,weditor)

pip install uiautomator2==2.16.23 weditor==0.6.8 -i https://pypi.doubanio.com/simple

# 在手机上安装 atx-agent 应用 

# 安装apk服务到手机上
python -m uiautomator2 init

脚本如下(可复制直接运行)

import os
 
# https://pypi.douban.com/simple  # 豆瓣镜像
# https://pypi.tuna.tsinghua.edu.cn/simple  # 清华镜像
 
mirror = " -i https://pypi.douban.com/simple"
 
os.system("python -m pip install --upgrade pip" + mirror)  # 更新 pip
os.system("pip install --pre -U uiautomator2" + mirror)  # 安装 uiautomator2
os.system("pip install --pre weditor" + mirror)  # 安装 weditor
os.system("python -m uiautomator2 init")  #安装 atx-agent 至手机

运行demo

import uiautomator2 as ui
import os
from loguru import logger

ui.DEBUG = False


class Connet(object):
    '''连接设备'''

    def __init__(self):
        self.devices_name_list: list = []  # 所有设备名称
        self.device: list = []  # 所有设备连接对象(每一个元素都是连接对象)
        os.environ['PATH'] += ';' + os.path.join(os.getcwd(), 'ADB')  # 设置adb为环境变量

    def _get_devices_name(self) -> None:
        '''提取设备名称'''
        devices_all = []
        for i in os.popen('adb devices').read().split('\n'):
            if i not in ('List of devices attached', ''):
                devices_all.append(i.split()[0])

        if len(devices_all) < 1:
            logger.error('检测不到设备')
        self.devices_name_list = devices_all

    def _connect(self):
        '''连接设备并提取设备对象至变量'''
        for device_name in self.devices_name_list:
            logger.warning(f'设备名称:【{device_name}】')
            self.device.append(ui.connect(device_name))
            logger.success(f'【{device_name}】...连接成功')

	def _start_vx(self):
        for device in self.device:
            try:
                device(scrollable=True).scroll.toEnd()   # 滑动至屏幕最底部
            except ui.exceptions.UiObjectNotFoundError as e:
                ...
            device(text='微信').click()  # 点击微信
           #  qujianma_assembly = device(resourceId="com.landicorp.jd.delivery:id/etPickUpCode")
           #  if not qujianma_assembly.exists:
           #      logger.error(f'找不到取件码组件,请切换至取件码页面')
           #     exit()

    def start(self, weishu):
        self._get_devices_name()
        self._connect()
        self._start_vx()
        
c = Connet()
c.start()

API方法详解

1.1连接设备
设备连接主要分为有线连接和无线连接,如下:

# 有线连接
d = u2.connect_usb(id)  # id 为 adb devices 命令中得到的设备 id
 
# 无线连接
d = u2.connect(ip)  # ip 为 手机 ip

说明:返回的 d 为连接句柄,通过 d 可以实现对手机的操作。

方法名称解析
d.app_install(‘http://domain.com/xxx.apk’)安装应用 【注】只能从 URL 安装
d.app_start(“app_package_name”, stop=False)打开应用(此处是包名)也可以用d(text='支付宝').click() ;stop参数为是否冷启动,默认为False
d.app_stop(“app_package_name”)关闭应用
d.app_stop_all()关闭所有应用
d.implicitly_wait(20)隐式等待20s,保证控件加载完成(可以全局设置)
d.app_uninstall(‘package_name’)卸载
d.app_current()获取当前正在运行的app的包名
d.app_info()获取app的信息
d.app_list_running()列出正在运行的app,这个也可以获取包名
d.app_clear(‘paceage_name’)清除app数据
d.info()获取设备基本信息
d.device_info()获取设备详细信息
d.window_size()获取设备大小
d.screenshot(‘d:/hello.png’)获取设备的截屏 传入电脑存放的路径
d.push(‘d:/hello.png’,‘/data/’)推送文件(上传文件) 第一个参数 PC需要上传的文件路径 第二个参数 手机端存放路径
d.pull(‘/data/hello.png’,‘d:/desktop/’)拉取文件和推送文件传参数相反
d.click_post_delay = 1.5全局设置每次单击UI后再次单击之间延迟1.5秒 默认无延迟
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
d(resourceId=元素ID).click()ID定位
d(text=“公众号:AirPython”).click()Text文本定位
d(description=“AirPython”).click()控件描述定位
d(className=“android.widget.TextView”).click()控件所属的类
d.xpath(“//*[@content-desc=‘AirPython’]”)Xpath定位
d(className=“android.widget.ListView”, resourceId=元素ID)组合定位
d(text=‘hello’, className=‘android.widget.TextView’)选text是’hello’,className 是’android.widget.TextView’ 的元素
d(text=“WiFi”).right(className=“android.widget.Switch”).click()选择"WiFi" 右侧的"switch" 同d(text="WiFi").right().click()left, right, top, bottom
d(text=“hello”, instance=0)获取第一个文本中带有“hello”的元素对象 同device(text="hello")[0]
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
d.wait_timeout = 10设置全局的超时时间 10s 只要设置了全局的超时时间,则其他的操作也会内置智能等待,不需要再进行任何操作
d.app_start(‘packagename’,wait=True)开启智能等待,即app完全打开后才执行后面代码
d.wait_activity()等待页面加载出来
d().wait()等待元素出现
d().wait_gone()等待元素消失
d().exists()等待元素是否存在
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
d.click()单击控件 支持xy坐标参数
d.double_click()双击控件 支持xy坐标参数
d.long_click(duration=0.5)长按控件支持xy坐标参数
d.set_text(text)输入文本
d.send_keys(text)输入文本广播式输入
d.set_fastinput_ime(True)打开关闭输入法 为True打开 否则关闭
d.clear_text()清除文本 d[index].clear_text() 定位的控件有多个,通过 index 指定某一个
d.get_text()获取文本 定位的控件只有一个,等价于 element[0].get_text()
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
device.screen_on()亮屏
device.screen_off()熄屏
device.press(“home”)首页
device.press(“back”)返回键
device.press(“left”)左移
device.press(“right”)右移
device.press(“up”)上划
device.press(“down”)下划
device.press(“center”)回到中间页
device.press(“menu”)菜单
device.press(“search”)搜索框
device.press(“enter”)回车键
device.press(“delete”)删除键
device.press(“recent”)打开最近的页面
device.press(“camera”)打开照相机
device.press(“power”)电源键
device.press(“volume_up”)声音调大
device.press(“volume_down”)声音调小
device.press(“volume_mute”)静音
d.orientation获取屏幕方向,取值为{“natural”, “left”, “right”, “upsidedown”}
d.freeze_rotation()锁定屏幕方向
d.freeze_rotation(True)解除锁定屏幕方向
d.set_orientation(“left”)向左转为横屏 d.set_orientation("right") # 向右转为横屏
device.screenshot(“screenshot.png”))截屏,支持自定义路径
d.screenrecord.start(“screenrecord.mp4”)录屏 需要从cv2 d.screenrecord.stop() 停止录屏
device.open_notification()打开通知栏

滑动操作

device(scrollable=True).fling(steps=5)   飞滑5,默认为1 手势向上,页面向下(正常翻页)
device(scrollable=True).fling.horiz.toBeginning()  横滑  手势向右,页面向左
device(scrollable=True).fling.toEnd()    飞滑到页面最底部


device(scrollable=True).scroll(steps=100)   滑动滚动条 距离为100
device(scrollable=True).scroll.horiz.forward(steps=100)  手势向左 页面向右
device(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000) 手势向右 页面向左
device(scrollable=True).scroll.toEnd()  滑动到末尾
device(scrollable=True).scroll.to(text="Security")  scroll 向前垂直,直到出现指定ui object 

device.swipe(500,500,100,500)
"""
swipe 平滑 
    第一种方式:需要传入四个参数
        startX:起始X坐标
        startY:起始Y坐标
        endX:结束X坐标
        endY:结束Y坐标
   """

"""
    第二种方式:需要传入两个参数
        direction(滑动的方向) : up、down、left、right
        scale(滑动的百分比)
"""
device.swipe_ext('left',scale=0.9)


"""
    第三种方式:先进行元素定位 再滑动
        direction(滑动的方向) : up、down、left、right
        steps(滑动的速度) ms
        d.swipe(sx, sy, ex, ey, duration=0.5)
"""
e = device(text='appname')
e.swipe('left',steps=100)

拖拽操作

d.drag(sx, sy, ex, ey, duration=0.5)

附录

#设置每次点击UI后再次单击之间延迟1.5秒
d.click_post_delay = 1.5
#设置默认元素等待超时(秒)
d.wait_timeout = 20








d.info
#得出设备链接信息
print(d.window_size())
#获取屏幕大小
print(d.current_app())
#获取当前应用的信息
print(d.serial)
#获取设备序列号
print(d.wlan_ip)
#获取WIFI IP
print(d.device_info)
#获取详细的设备信息







#打开/关闭屏幕
d.screen_on() 
#开启屏幕
d.screen_off() 
#关闭屏幕
d.info.get("screen") 
#获取屏幕开/关状态
#android>=4.4
d.press("home") 
#按下home键
d.press("back") 
#按下back键
d.press(0*07,0*02) 
#按下编码

'''支持按键模式'''
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.unlock()
#解锁屏幕
d.click(X,Y)
#点击屏幕坐标
d.long_click(x,y)
#长按屏幕
d.long_click(x,y,1)
#长按屏幕1s,默认是0.5,可自行配置
d.swipe(sx, sy, ex, ey)
#根据坐标滑动
d.swipe(sx, sy, ex, ey,1)
#根据坐标滑动,1代表滑动速度,默认0.5
d.drag(sx, sy, ex, ey)
#根据坐标拖动,适用于结算和滑块处理
d.drag(sx, sy, ex, ey, 1)
#根据坐标拖动,拖动时长1s,默认0.5


#截图
d.screenshot('1.jpg')
#截图保存在本地,文件名为1.jpg
#想获取其他格式的需要安装 pillow、numpy和cv2等库,具体不累述
d.open_notification()
#打开通知
d.open_quick_settings()
#打开快速设置
d.freeze_rotation() 
# 冻结旋转
d.freeze_rotation(False) 
# 开启旋转










'''检查特定的UI对象是否存在'''
d(text="Settings").exists 
# 返回布尔值,如果存在则为True,否则为False
d.exists(text="Settings") 
# 另一种写法
# 高级用法
d(text="Settings").exists(timeout=3) 
# 等待'Settings'在3秒钟出现
 d(text="Settings").info
# 获取特定UI对象的信息

'''获取/设置/清除可编辑字段的文本(例如EditText小部件)'''
d(text="Settings").get_text() 
#得到文本小部件
d(text="Settings").set_text("My text...") 
#设置文本
d(text="Settings").clear_text() 
#清除文本
d(text="Settings").center()
# 获取Widget中心点
#d(text="Settings").center(offset=(0, 0)) # 基准位置左前










d.push('1.txt','sdcard/downloacd')
#推送到文件下
d.push('1.txt','sdcard/downloacd/2.txt')
#推送并重命名到文件夹下
with open("foo.txt", 'rb') as f:
 d.push(f, "/sdcard/")
 #push fileobj
d.push("1.sh", "/data/local/tmp/", mode=0o755)
#推送并修改文件模式,在Python中表示八进制的友好方法默认0o755,文件权限设置
d.pull("/sdcard/1.txt", "1.txt")
#从设备侧拉取文件


'''定位方法'''
#text定位单击
d(text="Settings").click()
d(text="Settings", className="android.widget.TextView").click()
 
#resourceId定位单击
d(resourceId="com.ruguoapp.jike:id/tv_title", className="android.widget.TextView").click() 
 
#description定位单击
d(description="确定").click()
d(description="确定", className="android.widget.TextView").click()
 
#className定位单击
d(className="android.widget.TextView").click()
 
#xpath定位单击
d.xpath("//android.widget.FrameLayout[@index='0']/android.widget.LinearLayout[@index='0']").click()
 
#坐标单击
d.click(182, 1264)
















'''常用方法'''
# 等待10s
d.xpath("//android.widget.TextView").wait(10.0)
# 找到并单击
d.xpath("//*[@content-desc='分享']").click()
# 检查是否存在
if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists:
 print("exists")
# 获取所有文本视图文本、属性和中心点
for elem in d.xpath("//android.widget.TextView").all():
 print("Text:", elem.text) 
#获取视图文本
for elem in d.xpath("//android.widget.TextView").all():
 print("Attrib:", elem.attrib)
#获取属性和中心点
#返回: (100, 200)
for elem in d.xpath("//android.widget.TextView").all():
 print("Position:", elem.center())
 
'''xpath常见用法'''
# 所有元素
//*
 
# resource-id包含login字符
//*[contains(@resource-id, 'login')]
 
# 按钮包含账号或帐号
//android.widget.Button[contains(@text, '账号') or contains(@text, '帐号')]
 
# 所有ImageView中的第二个
(//android.widget.ImageView)[2]
 
# 所有ImageView中的最后一个
(//android.widget.ImageView)[last()]
 
# className包含ImageView
//*[contains(name(), "ImageView")]
# 等待10s
d.xpath("//android.widget.TextView").wait(10.0)

# 找到并单击
d.xpath("//*[@content-desc='分享']").click()

# 检查是否存在
if d.xpath("//android.widget.TextView[contains(@text, 'Se')]").exists:
 print("exists")
 
# 获取所有文本视图文本、属性和中心点
for elem in d.xpath("//android.widget.TextView").all():
 print("Text:", elem.text)
 
#获取视图文本
for elem in d.xpath("//android.widget.TextView").all():
 print("Attrib:", elem.attrib)
 
#获取属性和中心点
#返回: (100, 200)
for elem in d.xpath("//android.widget.TextView").all():
 print("Position:", elem.center())


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

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

相关文章

基于 Docker 部署 Mysql8.0.27_单机_主从复制

文章目录 单机部署集群部署master 部署slave 部署错误记录 单机部署 通过 dockerhub 或 docker search 查找镜像。拉取 mysql 镜像。 docker pull mysql:8.0.27创建挂载目录&#xff0c;并赋予权限。 mkdir -p /var/docker_data/mysql/data mkdir -p /var/docker_data/mysql/co…

一些关于c++的琐碎知识点

目录 bool强转 const构成重载:const修饰*p 移动构造 new int (10)所做的四件事 this指针---为什么函数里面需要this指针&#xff1f; .和->的区别 new创建对象 仿函数 new和malloc的区别 c系统自动给出的函数有 delete和delete[ ]区别何在 检查有没有析构函数 e…

六一,用前端做个小游戏回味童年

#【六一】让代码创造童话&#xff0c;共建快乐世界# 文章目录 &#x1f4cb;前言&#x1f3af;简简单单的弹球游戏&#x1f3af;代码实现&#x1f4dd;最后 &#x1f4cb;前言 六一儿童节。这是属于孩子们的节日&#xff0c;也是属于我们大人的节日&#xff08;过期儿童&…

chatgpt赋能python:**Python免费编辑器:提高开发效率和便捷性**

Python 免费编辑器&#xff1a;提高开发效率和便捷性 Python 编程语言已经成为了越来越多开发者的首选。这是因为 Python 语言非常直观易懂&#xff0c;同时也拥有庞大的第三方开源库&#xff0c;方便开发人员快速实现项目功能。Python 编程之所以如此受欢迎&#xff0c;除了这…

Java基础编程

Java入门 1. JDK的安装目录介绍 目录名称说明bin该路径下存放了JDK的各种工具命令。javac和java就放在这个目录。conf该路径下存放了JDK的相关配置文件。include该路径下存放了一些平台特定的头文件。jmods该路径下存放了JDK的各种模块。legal该路径下存放了JDK各模块的授权文…

使用kettle进行数据统计

1.使用kettle设计一个能生成100个取值范围为0到100随机整数的转换。 为了完成该转换&#xff0c;需要使用生成记录控件、生成随机数控件、计算器控件及字段选择控件。控件布局如下图所示 生成记录控件可以在限制框内指定生成记录的个数&#xff0c;具体配置如图所示 生成随机数…

chatgpt赋能python:Python免费教学:让编程更便捷

Python免费教学&#xff1a;让编程更便捷 Python是一门广泛应用于机器学习、数据分析、网络编程和自动化测试等领域的高级编程语言。随着人工智能和大数据分析的兴起&#xff0c;Python的应用广泛受到了各个行业的欢迎&#xff0c;越来越多的人开始学习Python。在这篇文章中&a…

STM32之任务的创建与删除

目录 1. 什么是任务&#xff1f; 2. 任务创建与删除相关函数 任务动态创建与静态创建的区别&#xff1a; xTaskCreate 函数原型 官方案例&#xff1a; vTaskDelete 函数原型 3. 实操 1. 什么是任务&#xff1f; 任务可以理解为进程 / 线程&#xff0c;创建一个任务&#…

【生物力学】《人体骨肌系统生物力学》- 王成焘老师 - 第2章 - 人体几何学测量与仿真建模

第1章回到目录第3章 文章目录 2.1 概论2.2 人体几何学测量2.2.1 人体外部几何形态2.2.2 人体尺寸测量与统计处理2.2.3 中国人体尺寸标准统计测量数据2.2.4 人体各部位比例及人体间尺寸换算2.2.5 人体活动范围测量 2.3 人体骨肌系统三维几何建模的数据来源2.3.1 冷冻切片数字摄像…

“微商城”项目(2准备工作)

一.安装Node.js 本项目使用Vue.js前端框架进行开发&#xff0c;在创建项目之前需要先下载Node.js环境。Node.js是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;它可以让JavaScript运行在服务器端。接下来对Node.js的下载和安装进行详细讲解。 打开Node.js官方网站&a…

RabbitMQ高频面试题

RabbitMQ的使用场景 异步发送消息&#xff08;验证码、短信、邮件…&#xff09;mysql、redis、es之间的数据同步分布式事务削峰填谷… 面试题&#xff1a;RabbitMQ如何保证消息不丢失 消息丢失原因 生产者发送的消息未到达交换机交换机未把消息路由到队列mq服务器宕机&…

辅助驾驶功能开发-功能算法篇(1)-ACC-多目标选择

1、总体架构 2、漏斗概念(Funnel) 路径漏斗是围绕着自车预测轨迹的一片区域。换言之,漏斗的中心应该根据预测的轨迹方向相应的弯曲。 2.1、普通&略宽漏斗(Normal Funnel & Wider Funnel) 普通漏斗用于选择路径中最近的目标车辆A和次近的车辆B。 更宽的漏斗用于选择…

@SentinelResource和openFeign+sentinel 对远程调用熔断降级加规则持久化的具体实 现

SentinelResource 自定义全局限流处理类 需求分析/图解 先看前面的一段代码 这个就是上面的Sentinel 热点规则 注意看我们的限制处理方法在本类中代码的耦合度高 阅读性差 不利于程序的扩展 SentinelResource的作用就是解决这个需求将处理方法放到一个类中 GetMapping("…

Docker从入门到会搭建一个简单的系统

一、环境准备 1、查看Llinux的版本 [rootlocalhost ~]# cat /etc/redhat-release 2、关闭防火墙 [rootlocalhost ~]# systemctl stop firewalld.service 3、设置开机不启动防火墙 [rootlocalhost ~]# systemctl disable firewalld.service 二、Docker的安装 1、Docker是中高…

单片机GD32F303RCT6 (Macos环境)开发 (三十二)—— GD32 SPI主从实验 中断模式

GD32 SPI主从实验 中断模式 1、接线 SPI0 PA4 PA5 PA6 PA7SPI1 PB12 PB14 PB14 PB15采用全双工模式SPI0 SPI1CS PA4----------------PB12CLK PA5----------------PB13MISO PA6----------------PB14MOSI PA7----------------PB152、spi0 主设…

chatgpt赋能python:Python简单游戏开发入门

Python简单游戏开发入门 Python作为一门流行的编程语言&#xff0c;可以用来开发各种各样的应用&#xff0c;包括游戏。虽然Python不是游戏开发的主流语言&#xff0c;但我们可以用它来制作一些简单而有趣的游戏。在这篇文章中&#xff0c;我们将介绍如何用Python创建一个简单…

批量剪辑视频工具源码开发搭建分享

搭建步骤 1. 首先需要根据自身产品确定视频类型及需要实现的视频效果 2. 根据预期视频效果选择视频上传模式&#xff0c;并将视频素材进行上传 3. 添加音频、字幕&#xff0c;标题等与素材进行组合。 4. 设置投放计划&#xff0c;包括&#xff1a;视频标题、视频话题等 5.…

Are Emergent Abilities of Large Language Models a Mirage?

Paper name Are Emergent Abilities of Large Language Models a Mirage? Paper Reading Note Paper URL: https://arxiv.org/pdf/2304.15004.pdf Video URL: https://www.youtube.com/watch?vhZspGdApDIo TL;DR 2023 年斯坦福的研究&#xff0c;探索大语言模型表现出涌…

LeetCode 1552. Magnetic Force Between Two Balls【二分,排序,贪心】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

内存栈与CPU栈机制

1. 内存栈: 先入后出,LIFO(LAST IN FIRST OUT) 入栈:将一个新的元素放到栈顶 出栈:从栈顶取出一个元素 栈顶元素总是最后一个入栈,需要时出栈. 2.CPU栈机制 8086CPU提供相关指令以栈方式来访问内存空间.相当于将一段内存当做栈来使用 8086CPU提供的入栈指令为:PUSH ,出栈指令为…