FXHao

个人站

欢迎来到我的小世界~哈哈哈~


Airtest入坑

Airtest入坑

简介

Airtets 是网易的 AirtestProject项目组推出的。是一款基于Python的、跨平台的 UI 自动化测试框架。具体的话是利用的是 图像识别 的原理。

Poco 是一款基于UI控件识别的自动化测试框架,目前支持Unity3D、Android原生、iOS原生。本质上也是python的第三方库。

AirtestIDE 顾名思义它是Airtest的代码编辑器,里面内置了Airtest和Poco的相关插件,可以帮助我们快速的编写代码。

相关文档

Airtest IDE的基础使用

  • 这款IDE还是非常友好的,它已经打包好了你使用时需要的全部环境,从官网下载好后就可以直接使用。

    1594829650102

连接设备

  • 首先是得让需要连接的设备在开发者模式中打开adb调试。

  • 当设备连接上时,这里会显示当前电脑所连接的所有设备(模拟器也是可以连接的)

    1594830092683

    然后点击connect就可以连接上设备,需要注意的是手机首次连接时会自动的安装几个软件:

    • PocoService:这个软件是帮助我们进行UI渲染的,如果发现UI树渲染不出来,首先检查这个软件有没安装,然后就是手机的权限监控
    • Yosermite:这个软件是与输入法相关的,所以当用完后,发现点击手机的输入框没有弹出输入键盘时,这个时候可以去设置里面把输入法改一下就行了

基本使用

  • 首先我们在AirtestIDE里新建一个脚本时,会在代码页里面自动生成以下配置

    1594832630644

    • auto_setup(__file__)

      image-20200716124701932

      这个这是一个用来初始化环境的接口,可接受4个参数,不传的话会使用默认的配置。

      __file__这个可以获取当前文件的路径然后传入。

  • 常用的接口

    1594833972492

    • 在Airtest辅助窗中,已经列出了一些的常用的API,点击的时候会自动帮我们生成代码,然后可以在右侧的设备屏幕上截取你需要识别的图像。鼠标放置在相关API上面可以查看该API所接受的参数及意义。

    • 然后还有其他许多的API,这些都是可以查询官方的文档和源码

      dev = device()  # 创建一个设备对象
      dev.shell()  # 在设备上运行shell命令
          
      wake()  # 唤醒设备
      start_app()  # 启动APP
      stop_app()  # 停止APP
      clear_app()  # 清除数据
      install() # 安装APP
      uninstall()  # 卸载
      home()  # 返回主界面
      
  • 图像识别配置

    1594836223677

    • 当我们截取到图像时,可以双击代码中的图像,然后就出现上面的配置窗,可以对当前图像的阈值、点击位置等等作出调整。上图中,点击左上角的按钮可以尝试匹配当前屏幕上的图像,若匹配成功,会标记出来。
    • 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 基础使用

image-20200716203531978

  • 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

  • 主要思路

    MTT2

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()