Airtest是由网易游戏推出的UI自动化测试解决方案,是一个跨平台的、基于图像识别的UI自动化测试框架,适用于游戏和App,支持平台有Windows、Android和iOS。

AirtestIDE对Python进行了封装,通过Airtest、Poco两个框架对应用元素进行定位,可以使用python的语法编写脚本。

  • 基于图像识别的Airtest框架,适用于所有Android/iOS/Windows应用
  • 基于UI控件搜索的Poco框架,适用于Unity3d,Cocos2d与Android/iOS App等多种平台

除了UIAutomator2外,还有Appium、AirTest等App自动测试框架。

自动化框架支持平台语言特点
AppiumAndroid、iOS、WindowsJava、Python、Ruby等跨平台,支持多种编程语言,是APP UI自动化领域的标杆产品
AirTestAndroid、iOS、WindowsPython基于图像识别和UI控件检索技术,‌适用于游戏和应用测试,上手简单,‌通过图像识别技术定位UI元素,‌无需嵌入代码即可进行自动化测试
UIAutomator2AndroidPythonAPI设计简洁易用,适用于较简单的原生Android应用,‌支持跨app操作,‌但无法使用录制功能,适用于对性能要求不高、‌操作简单的测试场景

本文是App测试自动化系列工具之UIAutomator2的使用。

注:由于Poco需要安装App对应的框架SDK(如Android、iOS、Unity、UE4、Qt等),所以虽然Poco写的脚本可以适应不同分辨率的终端,但本文只涉及Airtest图像识别框架的使用。


1. 安装AirtestIDE

AirtestIDE是绿色软件,无需安装,从官网下载页面下载AirtestIDE,解压后在AirtestIDE下找到AirtestIDE.exe,双击打开即可。

2. 安装python

AirtestIDE内置了Python环境,并安装了Airtest、Poco、airtest-selenium等库。如果在AirtestIDE内编辑、运行脚本,不需要额外安装环境。如果要部署本地的Python环境来跑脚本,首先要准备一个合适的Python环境,大于Python3,小于等于Python3.9均可。然后在Python环境里面,像安装其它Python第三方库一样,安装我们的自动化测试框架:

# 安装Airtest框架
pip install airtest

# 安装Poco框架;编写了Poco语句就需要安装
pip install pocoui

# 安装airtest-selenium框架;编写了airtest-selenium语句就需要安装
pip install airtest-selenium

更多运行环境配置请参考官方文档

3. Airtest项目文件结构

Airtest项目是一个以.air结尾的文件夹,其中的文件包括一个与文件夹同名(不含.air的部分)的.py脚本,以及用于图像匹配的图片若干。

一个典型的Airtest项目的目录结构如下:

├─AttackDarkArmyTeam.air    # 项目文件夹(必须以`.air`结尾)
│      AttackDarkArmyTeam.py    # 与项目文件夹同名的.py脚本(该脚本必须存在)
│      tpl1721443079684.png    # 用于图像匹配的图片1(可选)
│      tpl1721443091889.png    # 用于图像匹配的图片2(可选)
│      tpl1721443110665.png    # 用于图像匹配的图片3(可选)
│      ...    # 用于图像匹配的图片n(可选)

4. Airtest常用API

AirtestIDE左边的Airtest辅助窗有常用的API,点击对应的API,做出相应动作,即可生成代码。常用API及功能说明如下:

API类型功能说明
touch操作点击某个位置,可以设定被点击的位置、次数、按住时长等参数
wait操作等待某个指定的图片元素出现
swipe操作从一个位置滑动到另外一个位置
exists操作判断是否存在图片元素
snapshot操作对当前画面截图
text辅助输入文本
keyevent辅助输入某个按键响应,例如回车键、删除键
sleep辅助等待,就是python中的time.sleep()
assert_exists断言断言存在
assert_not_exists断言断言不存在
assert_equal断言断言相等
assert_not_equal断言断言不相等

5. 使用AirtestIDE编写代码

  1. 左上角,文件 -> 新建脚本 -> .air Airtest项目,选择项目文件保存地址;
  2. 右侧“设备窗”可以连接Android或iOS设备,也可以选择Windows窗口;
  3. 点击左侧Airtest辅助窗,按提示操作即可;
  4. 第3步会在“脚本编辑窗”生成代码,并且可以预览用于图像匹配的图片,图片被保存在脚本同级目录;
  5. 根据需要在“脚本编辑窗”编辑代码,封装函数等,完成脚本编写。
  6. 脚本编写完成后,在“脚本编辑窗”左上角的脚本文件名上右键,或点击“脚本编辑窗”右上角“+”号旁边的展开按钮,可以清理未使用的多余图片,压缩当前脚本。

注:使用Airtest图像匹配时,如果App窗口有缩放,可能导致图像匹配不上。

6. 代码样例

以某个小游戏的某种玩法为例,以下是.py脚本的代码,Template读取用于图像匹配的图片:

# -*- encoding=utf8 -*-

from airtest.core.api import *

auto_setup(__file__)

# 增加体力
def addPhysicalStrength():
    # 如果存在大体力,就用一次大体力
    if exists(Template(r"tpl1721444184608.png", record_pos=(0.003, 0.232), resolution=(449, 842))):
        # 点击选中大体力
        touch(Template(r"tpl1721444200238.png", record_pos=(-0.006, 0.078), resolution=(449, 842)))
        # 点击使用
        touch(Template(r"tpl1721443818854.png", record_pos=(0.001, 0.37), resolution=(449, 842)))
    # 如果没有大体力,那就看有没有小体力
    elif exists(Template(r"tpl1721444225899.png", record_pos=(-0.253, 0.232), resolution=(449, 842))):
        # 点击选中小体力
        touch(Template(r"tpl1721444239134.png", record_pos=(-0.255, 0.076), resolution=(449, 842)))
        # 点击使用
        touch(Template(r"tpl1721443818854.png", record_pos=(0.001, 0.37), resolution=(449, 842)))
    # 使用体力后,关闭窗口
    touch(Template(r"tpl1721467021987.png", record_pos=(0.415, -0.29), resolution=(449, 842)))

# 判断是否还有队列
def isNoMoreQueue():
    # 是否弹出了队列不足提示框
    if exists(Template(r"tpl1721444758059.png", record_pos=(0.008, -0.428), resolution=(449, 842))):
        # 关闭队列不足提示框
        touch(Template(r"tpl1721467021987.png", record_pos=(0.415, -0.29), resolution=(449, 842)))
        # 队列不足
        return True
    # 没有队列不足
    return False

# 寻找黑暗军团
def findDarkArmy(isPlus):
    # 当前界面是否存在黑暗军团
    if exists(Template(r"tpl1721446075178.png", record_pos=(-0.01, 0.029), resolution=(449, 842))):
        # 如果存在,不需要再找了
        return
    # 点击搜索按钮
    touch(Template(r"tpl1721443079684.png", record_pos=(-0.316, 1.362), resolution=(293, 842)))
    # 选择搜索类型
    touch(Template(r"tpl1721472679705.png", record_pos=(-0.36, -0.038), resolution=(449, 842)))
    # isPlus用来控制搜索敌军的等级
    if isPlus:
        # 搜索的敌军等级+1
        touch(Template(r"tpl1721450183747.png", record_pos=(0.273, 0.673), resolution=(449, 842)))
    else:
        # 搜索的敌军等级-1
        touch(Template(r"tpl1721450172927.png", record_pos=(-0.268, 0.67), resolution=(449, 842)))
    # 点击搜索
    touch(Template(r"tpl1721443091889.png", record_pos=(0.278, 1.222), resolution=(293, 842)))

# 攻击
def attack(isPlus):
    # 寻找黑暗军团
    findDarkArmy(isPlus)
    # 点击选中黑暗军团
    touch(Template(r"tpl1721446075178.png", record_pos=(-0.01, 0.029), resolution=(449, 842)))
    # 点击攻击五次
    touch(Template(r"tpl1721443110665.png", record_pos=(0.067, -0.29), resolution=(293, 842)))
    # 如果顺利进入攻击页面
    if exists(Template(r"tpl1721443138054.png", record_pos=(0.023, -0.131), resolution=(449, 842))):
        # 点击一键上阵
        touch(Template(r"tpl1721449556405.png", record_pos=(0.406, 0.557), resolution=(449, 842)))
        # 点击出征
        touch(Template(r"tpl1721443138054.png", record_pos=(0.023, -0.131), resolution=(449, 842)))
        # 返回攻击结果
        return True
    # 返回攻击结果
    return False

# 黑暗五连
def attackDarkArmyTeam(isPlus):
    # 如果出征成功
    if attack(isPlus):
        # 本次出击完成,返回success
        return 'success'
    # 如果缺少体力
    if exists(Template(r"tpl1721443799439.png", record_pos=(0.003, -0.29), resolution=(449, 842))):
        # 恢复体力
        addPhysicalStrength()
        # 攻击
        attack() 
    # 如果队列不够
    if isNoMoreQueue():
        # 返回wait,等待队列归队
        return 'wait'


if __name__ == '__main__':
    isPlus = True
    # 出兵成功后等待时间
    attackWaitInSecs = 50
    # 队列不足时的等待时间
    noQueueWaitInSecs = 10
    while True:
        try:
            ret = attackDarkArmyTeam(isPlus)
            if ret == 'wait':
                print("==========>has no more queue. wait " + str(noQueueWaitInSecs) + "s")
                sleep(noQueueWaitInSecs)
            else:
                print("==========>success. wait " + str(attackWaitInSecs) + "s")
                sleep(attackWaitInSecs)
            isPlus = not isPlus
        # 捕获异常,增强程序健壮性
        except Exception:
            # 如果有弹窗,关闭弹窗恢复到初始状态,尝试3次
            repeatCnt = 3
            while repeatCnt > 0:
                repeatCnt -= 1
                # 如果存在弹窗关闭按钮
                if exists(Template(r"tpl1721467021987.png", record_pos=(0.415, -0.29), resolution=(449, 842))):
                    # 点击弹窗关闭按钮
                    touch(Template(r"tpl1721467021987.png", record_pos=(0.415, -0.29), resolution=(449, 842)))

参考文档

  1. Airtest Project Docs
  2. Airtest的探索使用

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部