官网:http://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/
其他笔记:
Airtest Project 自定义启动器批量运行脚本
解决运行Airtest脚本时opencv-contrib-python报错的问题
开发环境准备
使用AirtestIDE来编写脚本,只需要在 官网 下载最新版本AirtestIDE,解压即可直接使用。AirtestIDE内置了Python3.6.5,airtest和poco环境,本地无需安装python环境就能直接使用。
如果想要使用其他需要安装的Python第三方库,或者Python2环境,则需要进行本地python的 环境部署 ,然后在AirtestIDE设置中添加本地的Python.exe路径,详情参考 IDE配置。
在AirtestIDE中新建的脚本,后缀都为 .air
,但实际上在运行的时候, 运行的是.air目录下的同名.py文件 。
脚本调试
目前AirtestIDE暂不支持断点调试功能,因此调试脚本只能通过print
等较为简单的方式。但是由于Airtest框架涉及到图像识别的准确率问题,需要反复运行和调试才能确定合适的图片与识别阈值,因此IDE特别提供了一种 选中部分代码单独运行
的调试功能(如下),需要注意的是,该单独运行代码的功能不会执行到脚本中其他代码里的内容,可能会出现别处的变量未能初始化等情况(因此不支持poco)。
IDE还提供了一个图片截图预览功能,在脚本编辑区内双击图片,会弹出图片编辑器,在图片编辑器内点击 Snapshot Recognition
按钮,将会截取当前的手机屏幕画面,并且进行一次识别,识别成功的话会在截图上面标注出识别区域,方便进行截图的调试。
Mac Airtest图片编辑器疑有BUG,点击图片编辑器的右下角的OK或Cancel键后,整个IDE都会卡死,已提交Issue
目前图片识别中,每次识别时,只要识别结果的可信度>阈值 threshold
即认为是识别通过。如果识别到错误的位置,可以通过调节 threshold
进行准确度调整。
Android 真机USB连接
测试机:华为
开启开发者模式:进入设置->系统->关于手机,多次点击版本号后进入开发者模式;
设置开发人员选项:允许USB调试、连接USB时总是弹出提示、监控ADB安装应用、仅充电模式下允许ADB调试(可选)
关闭电脑上已经安装的手机助手软件, 使用USB线连接手机,手机上出现USB连接方式(传输照片、传输文件和仅充电),选择"传输照片",如果在第二步中设置了仅充电模式下允许ADB调试,则需要选择"仅充电"选项;
点击Airtest点击列表内对应设备的 Connect 完成连接, 若设备未刷出,点击 refresh ADB 按钮设备列表将会刷新;
Airtest
基于图像识别的无侵入式测试框架。
初始化
# -*- encoding=utf8 -*-
from airtest.core.api import *
auto_setup(__file__) #自动初始化设备
录制airtest语句
- 辅助按键录制脚本
在AirtestIDE的Airtest录制辅助窗内,包含有三种类型的录制按钮:
点击某个按钮后,在设备画面上按下鼠标左键进行截图框选,抬起鼠标左键完成框选。对应操作语句会自动插入编辑器脚本中。
- 脚本自动录制
点击自动录制按钮后,使用鼠标操作设备画面,对应操作语句会自动插入到编辑器脚本中(不好用)。
airtest.core.api
Airtest的常用API大部分都列在了AirtestIDE里的Airtest辅助窗中,在使用各种常见的截图语句时,鼠标移动到按钮上即可看到每个接口的常用参数与返回值信息,非常方便。
Poco
基于UI控件识别的测试框架
API文档:https://poco.readthedocs.io/en/latest/source/poco.proxy.html#poco.proxy.UIObjectProxy.exists
poco.exceptions module: https://poco.readthedocs.io/en/latest/source/poco.exceptions.html
初始化
切换Poco应用类型时,脚本编辑框会弹出提示插入初始化Poco代码的通知窗。 确认Poco应用类型正确后,确认插入光标位置后,点击 'Yes'
即可插入对应的Poco初始化代码。
- Unity3D
from poco.drivers.unity3d import UnityPoco
poco = UnityPoco()
- Android native APP
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
poco = AndroidUiautomationPoco()
poco.device.wake()
poco(text='Clock').click()
切换模式
Poco Pause ->冻结模式
主要用途:查看当前画面中各位置的UI呈现范围。UI自动化测试中,常使用此功能进行元素定位。
进入方式:
单击UI渲染树上的任意条目(注意是单击,双击会直接将当前UI位置插入到代码区域);
-
点击 Poco Pause 按钮:
具体表现:
屏幕画面会冻结(设备操作失效),UI渲染树的数据也会停止刷新。
伴随鼠标在画面中的移动,对应位置的UI元素会被标记框标出。
鼠标左键点击,可以在log输出窗中查看对应控件的详细属性。
Poco Inspect ->检视模式
主要用途:伴随设备操作,查看不同页面UI的渲染情况。
进入方式:
具体表现:
设备画面可以正常交互,UI渲染树的数据正常刷新。
伴随鼠标在画面中的移动,对应位置的UI元素会被标记框标出。
鼠标左键点击,可以在log输出窗中查看对应控件的详细属性。
Poco Recording ->录制模式
-
单步录制Poco脚本
如下,先通过冻结模式在UI树中找到目标条目,双击直接插入poco代码,也可以右键目标条目,选择插入UI节点的Xpath代码。
注意:单步录制,只会poco插入代理对象,具体的操作代码(如
".click()"
)需要手动添加。 -
自动录制
进入方式:
具体表现:
设备画面可以正常交互,UI渲染树的数据正常刷新。
伴随鼠标在画面中的移动,对应位置的UI元素会被标记框标出。
伴随设备操作(点击、滑动),即可插入对应UI节点的poco代码。
自动录制时,会自动插入代码对象的操作代码。
选择器
在poco实例后加一对括号就可以进行UI选择了。选择器会遍历所有UI,将满足给定条件的UI都选出来并返回。括号里的参数就是所给定的条件,用属性名值对表示,其中第一个参数固定表示 节点名 其余可选参数均表示节点的属性及预期的属性值。详见: API Reference selecting UI
- Basic Selector
# select by node name
poco('bg_mission')
# select by name and other properties
poco('bg_mission', type='Button')
# 支持正则
poco(text='确定')
poco(textMatches='^据点.*$', type='Button', enable=True)
- Relative Selector
# select by direct child/offspring
items=poco('main_node').child('list_item').offspring('item')
# 可迭代对象
for item in items:
item.child('icn_item')
- Sequence Selector
tems = poco('main_node').child('list_item').offspring('item')
print(items[0].child('material_name').get_text())
print(items[1].child('material_name').get_text())
读取属性
mission_btn = poco('bg_mission')
print(mission_btn.attr('type')) # 'Button'
print(mission_btn.get_text()) # '查询停车费'
print(mission_btn.attr('text')) # '查询停车费' equivalent to .get_text()
print(mission_btn.exists()) # True/False, exists in the screen or not
操作对象
- 点击 click
点击默认点在 anchorPoint 上,每个UI都会有一个 anchorPoint ,也就是检视器(Inspector)中UI包围盒的那个红点,大部分情况下 anchorPoint 都在UI包围盒的正中央。如果想指定其他的点击位置,可以传一个参数到click
方法中,这个参数是一个用list或tuple表示的2维向量,其 [x, y] 值分别表示相对于包围盒左上角的偏移量,左上角为[0, 0]
,右下角为[1, 1]
poco('bg_mission').click()
poco('bg_mission').click('center')
poco('bg_mission').click([0.5, 0.5]) # equivalent to center
poco('bg_mission').focus([0.5, 0.5]).click() # equivalent to above expression
- 局部定位 focus
所有UI相关的操作都默认以UI的 anchorPoint 为操作点,如果想自定义一个点那么可以使用focus
方法。调用此方法将返回 新的 设置了默认 焦点 的UI,重复调用则以最后一次所调用的为准。focus
所使用的是局部坐标系,因此同样是UI包围盒的左上角为原点,x轴向右,y轴向下,并且包围盒长宽均为单位1。很显然中心点就是[0.5, 0.5]
.
poco('bg_mission').focus('center').click() # click the center
- 滑动 swipe
swipe操作同样是以 anchorPoint 为起点,如果你想改变起点请使用focus
方法,然后朝给定向量所代表的方向滑动,距离也就是向量的长度.
joystick = poco('movetouch_panel').child('point_img')
joystick.swipe('up')
joystick.swipe([0.2, -0.2]) # swipe sqrt(0.08) unit distance at 45 degree angle up-and-right
joystick.swipe([0.2, -0.2], duration=0.5)
- 拖拽 drag
与swipe
不同的是,darg
是从一个UI拖到另一个UI,而swipe
是将一个UI朝某个方向拖动。
poco(text='AAA').drag_to(poco(text='BBB'))```
将 focus
和 drag_to
结合使用还能产生卷动(scroll)的效果,下面例子展示了如何将一个列表向上卷动半页。
scrollView = poco(type='ScollView')
scrollView.focus([0.5, 0.8]).drag_to(scrollView.focus([0.5, 0.2]))
- UI等待
在给定时间内等待一个UI出现并返回这个UI,如果已经存在画面中了那就直接返回这个UI。 如果超时了还没有出现,同样也会返回,但是调用这个UI的操作时会报错。(我真被这里坑死了)
poco('bg_mission').wait(5).click() # wait 5 seconds at most,click once the object appears
poco('bg_mission').wait(5).exists() # wait 5 seconds at most,return Exists or Not Exists
- 截图 snapshot
截屏幕并以base64编码返回。截图的格式(png, jpg, …)由对应的sdk实现决定,大多数情况下是png。
from base64 import b64decode
b64img, fmt = poco.snapshot(width=720)
open('screen.{}'.format(fmt), 'wb').write(b64decode(b64img))
- 全局操作
在没有选定或指定UI的情况下也可以进行操作(模拟输入),也叫全局操作。
poco.click([0.5, 0.5]) # click the center of screen
poco.long_click([0.5, 0.5], duration=3)
# swipe from A to B
point_a = [0.1, 0.1]
center = [0.5, 0.5]
poco.swipe(point_a, center)
# swipe from A by given direction
direction = [0.1, 0]
poco.swipe(point_a, direction=direction)
常见异常
from poco.exceptions import PocoTargetTimeout
from poco.exceptions import PocoNoSuchNodeException
使用命令行运行脚本
官方文档: http://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/4_run_script/run_script.html#id3
运行脚本:
PS E:\treasure\Airtest> [python -m] airtest run parkIndex.air --log E:/CatJmx/ParkTest/Airtest/log
可选参数:
- [--recording]
让airtest自动对脚本执行过程中的手机屏幕进行录制操作。录制完成后,将自动生成一个命令格式类似于 recording_0.mp4 的文件到脚本生成的log目录中。在最后生成报告时,这个mp4文件会默认显示在HTML报告页面里。
- [--device]
设备字符串,什么都不填写,会默认取当前连接中的第一台手机;字串完整定义格式:Android://
- [--log]
指定生成的log目录路径,默认为脚本所在目录
生成报告
脚本运行过程,与报告生成过程是独立的两个步骤,因此在运行过 airtest run script.air后,假如没有指定 --log log/ 参数,Airtest 将把生成的log内容放到当前命令行的执行目录里(如果指定了 --log 参数,log内容与截图将会放在指定目录里)
PS E:\treasure\Airtest> [python -m] airtest report parkIndex.air --log_root E:/CatJmx/ParkTest/Airtest/log --outfile log/log.html --lang zh --export E:/CatJmx/ParkTest/Airtest --plugin poco.utils.airtest.report
可选参数:
[--log_root]
日志根目录,日志文件应该是 log_root / log.txt[--outfile]
指定输出html文件路径,默认为log.html[--static_root]
静态文件根目录[--lang zh]
设置报告语言[--export]
在使用 airtest report 指令生成的报告中,使用了绝对路径来访问里面的图片文件,同时HTML报告中访问的静态css与js资源文件,也是硬盘上的绝对路径(默认在airtest的安装目录下的report文件夹里)。因此,假如想要发送报告给其他人观看,就必须要在命令行末尾加上 --export 导出目录,将报告导出到一个指定目录中,然后将整个目录发送给别人查看;[-plugin poco.utils.airtest.report]
默认报告是airtest的专属报告,对于poco语句的支持不够完善,需要使用插件的形式来补充;-
[--plugin airtest_selenium.report]
如果脚本中使用了selenium插件,在生成报告的命令行最后,需要加入 --plugin airtest_selenium.report,可以让报告支持selenium元素;
[To be continued...]