Airtest Project 是为编写自动化脚本,达到提升测试效率的一整套解决方案。它可以轻松的扩展到多平台、多引擎上;如基础的 Android和IOS手机应用、App;Windows上的应用等。
学习使用 Airtest Project 很容易,由于 Airtest Project 是基于Python的,只需要会一点基础的 Python 基础知识即可。Airtest Project 需要一个开发环境,推荐使用配套的 AirtestIDE;AirtestIDE针对于 Airtest Project 有一些特殊的功能,使用别的环境可能会让你开发时工作繁琐,效率降低等。
Airtest Project 包含了两个框架,一个是 Airtest 一个是 Poco,这两个框架都是Python 的第三方库。在开发过程中,可以在开发时引入其它库加强你的脚本。
Airtest
是一个跨平台的、基于图像识别的UI自动化测试框架,适用于游戏和App,支持平台有Windows、Android和iOS——引于官方文档
Airtest 可实现“即看见可操作”,但是对文本内容的获取缺无能为力;这一点在官方文档中也有说明。这一点缺点也有解决办法:可通过引入文字识别库进行补缺,如:pytesseract。
使用 Airtest 进行自动化测试时,操作流程一般为:图片截取 → 图片对比 → 相似度与设定值对比 → 找出坐标位置 → 点击。默认情况下 Airtest 对于不同颜色的对比并不敏感,需要开启颜色对比。
在测试对象非原生App或无法取得项目源码时使用 Airtest 进行测试是个很好的选择。
Poco
是一款基于UI控件识别的自动化测试框架,目前支持Unity3D/cocos2dx-*/Android原生app/iOS原生app/微信小程序,也可以在其他引擎中自行接入poco-sdk来使用——引于官方文档
在已有项目源码或测试对象为原生App时使用Poco进行自动化测试,不仅满足可对文本的获取,而且相比 Airtest 更为简洁!本篇只讲解 Airtest 的操作。
AirtestIDE
是一个我们配套推出的跨平台的UI自动化测试编辑器,内置了Airtest和Poco的相关插件功能,能够使用它快速简单地编写脚本——引于官方文档
使用 AirtestIDE 将极大的简便我们的开发过程,对开发者非常友好。提供了截图及截图预览、可连接设备自动读取、高亮的编辑界面、脚本录制、支持设备远程连接并且在嵌入设备对象窗口实时刷新。
安装 AirtestIDE 后,打开 AirtestIDE ,打开模拟器中需要测试的App。
AirtestIDE 的设备窗口默认在可是界面的最右边。
在 AirtestIDE 中,界面元素可以拖拽,布置成你所喜爱的界面风格。假设一些窗口无意中关闭,可在窗口下拉选项中打开窗口。
连接设备只需要在移动设备的窗口下列表点击出现的设备信息中的connect,即可连接。
假设设备列表并未出现设备,点击刷新;
如果是使用真机设备请使用USB线连接手机,并且允许USB调试,之后刷新ADB。
远程连接需要只是IP及端口号,填入字段点击连接即可。
更多链接本文不再赘述。可查看官方文档
我当前使用的设备为模拟器设备,模拟器连接过程直接在出现的设备列中点击connect即可:
在 Airtest 开发中是以“.air”作为文件后缀。
连接设备后,查看代码:
其中 将Airtest的基本API引入,为之后编写做好准备。
查看可视窗口最左侧,有Airtest辅助窗与Poco辅助窗,本篇主要讲解Airtest。
首先尝试第一个操作touch,touch中文译为“触摸”,从命名上得知,这是个可实现“触碰”功能的操作。首先鼠标悬浮在 touch 选项处:
将会提示 touch 功能的相关信息,现在简单的尝试一下 touch 功能。
点击 touch ,把鼠标移动到设备窗,找到你想要实现点击的按钮,点下左键不放,进行拖拽选中,随后放手。
这时,代码编辑区将会出现 ,点击运行脚本,尝试使用:
运行效果如下:
从效果中可以看到 touch 将会找到与我们所选中的图形相似的图案,进行计算匹配,达到匹配的要求后,进行点击操作。
现在查看一下 touch 函数的实现,从中得到更多的信息,帮助我们进行脚本的开发;点击文件名,然后选择“打开当前文件项目目录”:
找到当前文件目录后,找到与文件名相同的 .air 文件,使用编辑器进行打开。
以下为编辑器打开该该文件后的代码:
在不经过 AirtestIDE 处理的代码中,图片的表现形式为路径,以及使用了 Template 作为处理,此处,Template 函数接收3个函数,分别为:图片路径 ecord_pos以及resolution。
在 Airtest api 文档中查询 Template 方法。
查看文档的值,刚刚使用的Template将会直接使用参数初始化一个类。
其中参数查看文档得知:
- filename:文件路径
- threshold:图像识别阈值,是用来判定一张图片识别是否成功的阈值,例如一张图片识别到的匹配度是0.65,而我们设置的threshold为0.7的话,Airtest会认为匹配失败,从而进行下一次匹配。
- target_pos:图像点击位置,当识别出一张图像后,Airtest将会默认去点击图像的正中心位置,有时我们希望它识别出图片后点击其他位置,可以通过修改target_pos属性来实现。
- rgb:切换彩色与灰度识别,在识别图像时,Airtest会先将图像转为灰度图再进行识别。因此假如有两个按钮,形状内容相同,只有颜色不同的情况下,Airtest将认为它们都是相同内容。
通过勾选rgb选项,或在代码中加入rgb=True,可以强制指定使用彩色图像进行识别。
其中参数,还差 record_pos 与 resolution;以下为Template类,查看文档得知: - resolution:录制时的屏幕分辨率
- record_pos:录制时屏幕上的坐标
通过以上分析的值,在代码:
其中 resolution 为当前设备的分辨率为:540, 960;可是这和我设置的分辨率不一样,查看文档得知:“在使用不同分辨率的设备进行图像识别时,可能会导致识别成功率不佳,因此Airtest提供了默认的分辨率适配规则”。从中也得到了些许信息,如“使用缩放后是否不精确?”,当然,文档也给出了解决方案:“想要提高2d游戏的识别精度,最好的办法就是明确指定你的游戏的分辨率适配规则;下面的代码指定了一个自定义的缩放规则:直接return原来的值,不管屏幕分辨率,所有UI都不进行缩放。”,
代码如下:
这里的RESIZE_METHOD,即我们定义的custom_resize_method使用的输入参数为:
- w, h # 录制下来的UI图片的宽高
- sch_resolution # 录制时的屏幕分辨率
- src_resolution # 回放时的屏幕分辨率
以上分析得知,通过Template示例后一个对象,作为参数传给touch方法,那么touch方法应该进行剩下的图片查找及触摸操作;继续分析touch方法。
以下在文档中找到touch方法:
文档中说明,touch方法为在设备屏幕上执行触摸操作。参数有:
- 一个目标,这个目标可以是 Template 的实例或者是一个坐标;
- 执行多少次点击
- 按照平台的不同所需的不同参数
- 最终返回位点击的坐标
- 适用平台为 Android, 、Windows 、iOS
点击源代码查看实现:
经过之前的分析,得知 touch 将会执行查找图片和点击的操作;从实现中得知:
传入参数后,首先判断传入的对象 v 是否属于 Template对象,是这个对象,执行 loop_find方法,传入对象,设置超时为 ST.FIND_TIMEOUT,然后把查找得到的坐标给予 pos 变量。
之后使用循环实现点击,循环1次点击1次,循环2次点击2次,以此类推,调用G.DEVICE.touch 方法,传入 pos 及 kwargs 进行点击。
查看 G 类具体实现,G在helper中,查看helper:
其实在这里,已经注册过了设备,默认的编辑窗口已经隐藏了这个过程,我们点击新建文件可以看到 auto_steup(),该方法实现在 airtest.core.api 中,其中auto_steup()方法定义如下:
其中所需的 import_device_cls 方法在 airtest.core.helper中:
很清楚的看到,在 auto_setup 中有层级的调用了connect_device进行设备连接初始化,在connect_device中调用import_device_cls添加设备,随后使新设备在G类中赋值给G.DEVICE,最后传给G.DEVICE_LIST。
在这里出现了 DEVICE_LIST 给对多设备操作的方式有了可能性,当然 Airtest Project 本就是这么一个解决方案。在文档中就有多机协作的介绍。以下文字引于文档。
在我们的脚本中,支持通过 set_current
接口来切换当前连接的手机,因此我们一个脚本中,是能够调用多台手机,编写出一些复杂的多机交互脚本的。在命令行运行脚本时,只需要将手机依次使用 --device Android:/// 添加到命令行中即可,例如:
当然多设备并行的方案现在也有很多之后补充。
最终,调用 airtest.core.android.android 中 touch 完成点击:
实现如下:
以上就是简单的一个 touch 完成的所实现的过程。
我们现在就来尝试开启颜色识别以及阀值设置:
增加 if 判断,判断是否存在图片,存在则点击,并且提高阀值以及开启颜色识别:
双击图片进行更改值:
去代码查看是否改动
最后优化一下,根据流程编写了如下脚本:
其中程序代码为:
运行结果如下:
以上脚本使用了 exists 断言,判断图片是否存在,存在返回 pos 坐标点,不存在返回False:
使用 exist 判断可以当做为脚本逻辑的一个分支,存在,则执行之后的操作,不存在。在使用 exist 时使用if,在同级下,多个if可以有效的让所有情况出现不交叉的分支,使脚本代码结构清晰是个不错的选择!
以上脚本还存在一个小尾巴,那就是在结尾处点击训练后,自动返回主目录。修改如下:
为了时脚本保持健壮性,我在点击训练意外情况找不到时,用了else语句,使其返回。
为了更好的深入理解脚本,我们查看一下 exists 的实现;exists 的实现在 airtest.core.api 中:
exists 将会在屏幕中查找目标,如果找到将会返回坐标值。
在这里我们已经是第二次看见 loop_find 方法,此方法是 Airtest 的核心方法。loop_find 方法实现于 airtest.core.cv :
文档 对于 loop_find 有些接口介绍:
query:截图对象
timeout:超时
threshold:阀值,也就是对比后的相似度的值,越大越难匹配,要求精度越高
interval:匹配相距时间
intervalfunc:失败后的响应
在执行 loop_find 时首先给个计时器计时,获取屏幕后验证屏幕是否为None,为None可能没连接上;屏幕获取无异常则,使用截图对象调用 match_in 方法,成功进行匹配返回坐标值,否则返回False。
其中主要方法为 match_in ,match_in 也在 airtest.core.cv : 中,定义如下:
_cv_match 如下:
其中在 match_in 中调用了 _cv_match,_cv_match 首先对图片 _imread() 进行CV2 处理,然后后面对图片进行压缩,应该是通过文档中所说的通过COSCOS的规则。然后根据一个策略遍历里面的算法进行计算,最后得到 ret 返回计算结果。(在深度就不会了,毕竟我不是搞测试的,点到为止)
Airtest 的核心浅显流程搞清楚了,我们得知,在进行 touch 及 exists 时都会进行 loop_find 合理的调整查询阀值和RGB开启可以有效的节省匹配时间。优化脚本,合理的把部分图片的RGB关闭以及部分图片阀值减小以提升脚本运行效率。
修改后脚本如下:(修改了2个按钮,降低了阀值及关闭了RGB)
运行结果(没有加倍数,确实快了很多):
使用循环让程序一直挂机吧!
修改程序如下:
结果发现在程序正常运行后,逻辑出现错误,运行结果如下:
这时需要修改程序,把头盔的判断增加else分支,改为:
结果再次运行发现训练过后,第二次训练时间变成等待时间:
改为先判断后点击,头盔也是:
这次就很完美了:
那我再按照游戏左下角提示操作去完成另外的逻辑,一共2个分支在运行判断:
运行如下:
可能某些情况需要拖拽屏幕,这个使用需要使用:
使用 swipe 推荐对于坐标系不熟的使用录制脚本功能编写,通过这个功能,可以快速的写好脚本:
点击后,进入录制脚本状态,这个时候直接在屏幕上进行拖拽即可,记得幅度不要过大,不然在运行时导致滑动过多。