Airtest入坑
简介
Airtets 是网易的 AirtestProject项目组推出的。是一款基于Python的、跨平台的 UI 自动化测试框架。具体的话是利用的是 图像识别 的原理。
Poco 是一款基于UI控件识别的自动化测试框架,目前支持Unity3D、Android原生、iOS原生。本质上也是python的第三方库。
AirtestIDE 顾名思义它是Airtest的代码编辑器,里面内置了Airtest和Poco的相关插件,可以帮助我们快速的编写代码。
相关文档
Airtest IDE的基础使用
-
这款IDE还是非常友好的,它已经打包好了你使用时需要的全部环境,从官网下载好后就可以直接使用。
连接设备
-
首先是得让需要连接的设备在开发者模式中打开adb调试。
-
当设备连接上时,这里会显示当前电脑所连接的所有设备(模拟器也是可以连接的)
然后点击connect就可以连接上设备,需要注意的是手机首次连接时会自动的安装几个软件:
- PocoService:这个软件是帮助我们进行UI渲染的,如果发现UI树渲染不出来,首先检查这个软件有没安装,然后就是手机的权限监控
- Yosermite:这个软件是与输入法相关的,所以当用完后,发现点击手机的输入框没有弹出输入键盘时,这个时候可以去设置里面把输入法改一下就行了
基本使用
-
首先我们在AirtestIDE里新建一个脚本时,会在代码页里面自动生成以下配置
-
auto_setup(__file__)
:这个这是一个用来初始化环境的接口,可接受4个参数,不传的话会使用默认的配置。
__file__
这个可以获取当前文件的路径然后传入。
-
-
常用的接口
-
在Airtest辅助窗中,已经列出了一些的常用的API,点击的时候会自动帮我们生成代码,然后可以在右侧的设备屏幕上截取你需要识别的图像。鼠标放置在相关API上面可以查看该API所接受的参数及意义。
-
然后还有其他许多的API,这些都是可以查询官方的文档和源码
dev = device() # 创建一个设备对象 dev.shell() # 在设备上运行shell命令 wake() # 唤醒设备 start_app() # 启动APP stop_app() # 停止APP clear_app() # 清除数据 install() # 安装APP uninstall() # 卸载 home() # 返回主界面
-
-
图像识别配置
- 当我们截取到图像时,可以双击代码中的图像,然后就出现上面的配置窗,可以对当前图像的阈值、点击位置等等作出调整。上图中,点击左上角的按钮可以尝试匹配当前屏幕上的图像,若匹配成功,会标记出来。
- threshold:阈值,这个数据与图像识别精准度关系非常大,在图像的二值化处理中,阈值相当于一个临界值,然后大于这个临界值就转换成白色,反之就转换成黑色。在这里,可以理解为阈值和图像识别的精确度成正比,但相对应的是识别的成功率也会降低,这个可以根据实际情况来取设置。
-
全局配置设置
-
像上面说的图像的配置设置只是修改点击的那张图片的配置,而在Airtest中有专门的全局配置,我们可以直接修改全局配置来同一修改我们需要的数据。
-
在
airtest.core.api
接口中有一个ST
变量,它就是全局设置from airtest.core.api import * ST.THRESHOLD = 0.7 # 修改阈值[0,1] ST.OPDELAY = 0.1 # 操作之间的间隔时间(s)[0,1] # touch()、wait()、assert_exists()等函数是读取这个配置的 FIND_TIMEOUT = 20 # 图片查找超时时间 # exists()、assert_not_exists() FIND_TIMEOUT_TMP = 3 # 可以通过设定一个默认项目根目录PROJECT_ROOT,让使用using接口时能够在当前根目录下寻找别的子脚本 ST.PROJECT_ROOT = "/User/test/project"
-
Poco 基础使用
-
Poco可以检测多种平台的UI控件,但需要事先接入相对应的SDK,Android的话可以直接使用,在选择Android后,在代码编辑框内会自动导入相应模块和初始化(点击‘yes’),Poco辅助窗内会渲染出手机当前界面的控件信息。
from poco.drivers.android.uiautomation import AndroidUiautomationPoco poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
Poco 基本语法
- 元素定位
poco()
:括号里面就是筛选的控件的信息条件poco('a').child()
:获取“ a ”节点下的子节点,多个子节点的话需要用循环提取,否则默认提取第一个子节点,同时child
里面也可以填写过滤条件poco(textMatches='正则')
offspring()
:获取孙节点,同样,如果是多个的话需要用for循环提取sibling()
:获取当前节点的兄弟节点parent()
:获取当前节点的父节点
- 节点属性内容操作
get_text()
:提取文本内容poco().attr('xxx')
:获取空间的各个属性,attr()
内填写需要获取的属性名set_text()
:输入字符串
- 等待UI
wait()
wait_for_all([a,b,c])
:等待多个同时出现(默认120s)wait_for_any([a,b,c])
:等待其中一个出现wait_for_disappearance()
:等待摸个元素消失exists()
:判断是否存在
- 控件操作
click()
:点击long_click()
:长按drag_to(poco('xx'))
:从一个元素拖动到另一个元素
小demo1
-
主要思路
import logging
logger = logging.getLogger("airtest")
logger.setLevel(logging.ERROR) # 设置日志输出等级
class CheckCollapse(object):
'''检测MTT2应用宝崩溃率'''
def __init__(self):
self.app_packagename = 'com.tencent.tmgp.mytalkingtom2'
self.app = device()
self.start_num = 0 # 记录APP启动次数
self.collapse_num = 0 # 记录APP崩溃次数
def output(self):
'''输出'''
print('***************启动APP次数:【{}】*******************'.format(self.start_num))
print('***************崩溃次数:【{}】***********************'.format(self.collapse_num))
print('***************崩溃率为:【{}%】**********************'.format((self.collapse_num * 100 / self.start_num)))
def login(self):
'''登录操作'''
wait(Template(r"tpl1594182982780.png", record_pos=(0.006, 0.875), resolution=(1200, 2640)))
sleep(1)
# touch((322, 2401))
touch((296,2078))
sleep(1)
wait(Template(r"tpl1594183068639.png", record_pos=(0.007, 0.224), resolution=(1200, 2640)))
sleep(1)
# touch((589, 2342))
touch((500,2035))
sleep(1)
wait(Template(r"tpl1594183146750.png", record_pos=(0.0, -0.133), resolution=(1200, 2640)), timeout=5)
sleep(1)
# touch((1094, 254))
touch((989,189))
sleep(1)
def check_app(self):
'''检测APP进程是否存在'''
try:
# 判断APP进程是否存在
str = 'ps|grep ' + self.app_packagename
self.app.shell(str)
except:
self.collapse_num += 1
# 获取日志
with open('./MTT2_log_{}.txt'.format(self.start_num), 'wb') as f:
for x in self.app.logcat(grep_str=self.app_packagename):
f.write(x)
def main(self):
'''主体流程'''
while (True):
start_app(self.app_packagename)
self.start_num += 1
try:
# 尝试第一次登录
self.login()
# 尝试第二次登录
self.login()
except:
self.check_app()
finally:
stop_app(self.app_packagename)
self.output()
sleep(30)
小demo2
主要思路:首先获取聊天窗口的中送餐链接的元素,因为新的链接在最下面,为优先点击最新链接,需对获取到的链接进行反转,所以用一个列表来存储链接元素,插入列表的时候用索引的插入到最前面,在读取元素的时候最先读取的就是最后插入进去的,也就是最新的链接信息。然后为防止多次点击已经点击过的链接,发现每个送餐链接都有一个送餐人的名字,可以把name提取出来作为每个链接的唯一标识,用一个列表存储起来,下次点击链接之前先根据name判断之前是否点击过,没点击就进行点击,点击过就进行过滤,然后在聊天界面每过一秒检测一次有无新的送餐链接。
def grab_food():
'''抢餐'''
start_app('com.tencent.mm')
poco(text='金科互动娱乐广州大群').wait().click()
# 存放送餐人姓名
name_list = []
# 存储送餐数据
msg_list = []
while(True):
sleep(1) # 隔一秒检测一次
# 数据反转
try:
with poco.freeze() as poco_freeze:
for food in poco_freeze("com.tencent.mm:id/an3").offspring(textMatches='^.*送给你一份美味的餐品啦~'):
msg_list.insert(0, food)
except:
continue
# 读取数据
for msg in msg_list:
name = msg.get_text().split('送')[0]
if name not in name_list:
name_list.append(name)
msg.click()
if poco(text='立即领取').wait(3).exists():
poco(text='立即领取').click()
print('********抢到啦~加餐!!********')
poco("com.tencent.mm:id/dn", desc='返回').wait().click()
else:
print('********被抢啦~减肥!!********')
poco("com.tencent.mm:id/dn", desc='返回').wait().click()