Airtest 学习 Airtest 的一些经验分享,新手指引部分

啵板 · 2018年07月28日 · 最后由 啵板 回复于 2019年07月31日 · 4879 次阅读
本帖已被设为精华帖!

首先感谢网易的各位大神,从 ATX 到 Airtest 收获颇多

在手游中,一般新手指引都是固定的强制步骤,所以我们可以用自动点击的方式直接跑完整个新手

现行方案

  • 基于坐标(相对位置)
    1. 直接点击每个步骤对应的坐标位置,每个步骤量一下坐标
    2. 更换不同分辨率的手机,只需要计算一下,改成点击屏幕的相对位置就行了
    3. 点击坐标过程中如果遇到弹框(断线)之类的阻碍时,没办法跳过
  • 基于图片
    1. 在当前界面搜索图片,点击改图片的中心(但是图片识别存在匹配度的问题,可能会识别的相似的图片)
    2. 更换不同分辨率的手机,每种分辨率都需要截图,工作量比较大(airtest 有提供缩放的方法,可以只截一种分辨率)
    3. 点击坐标过程中如果遇到弹框(断线)之类的阻碍时,只需要提前截图,在点击过程中增加判断
  • 基于 UI
    1. 在当前场景内搜索 UI 对象,点击改对象的位置(要注意条件,可能搜索条件并不是唯一;搜索到的 UI 也有可能并没有显示在屏幕内)
    2. 更换不同分辨率的手机,不需要做其他操作
    3. 点击坐标过程中如果遇到弹框(断线)之类的阻碍时,只需要增加判断就可以了

以上三种方法,在编写测试代码的时候都需要手动跑至少一遍新手测试
坐标的话,需要测量计算
图片的话,主要的工作量在截图
UI 对象的话,只需要测需要的 UI 就可以了(可能并不需要手动跑完整个新手)
对比下来,UI 的工作量是最小的;而且以上三种方法可以结合使用
而且使用 UI 对象有个最大的优势,可以取到游戏内的文本,可以进行更复杂的逻辑操作

代码示例

学 ATX 时写的基于图片操作,完全写死的步骤

# -*- encoding: utf-8 -*-

import atx
from atx.ext.report import Report # report lib
import time

game_name = u"xxx"  隐藏了
device_name = u"84B7N16620000490"
package_name = u"xxxxx" 隐藏了
activity_name = u"xxxxx"    隐藏了
pic_url = u"C:\\Users\\HBoPRC\\xxx\\"

d = atx.connect(device_name)
rp = Report(d, save_dir = pic_url + "Report\\")
rp.patch_uiautomator() # for android UI test record (optional)
rp.info(u"Test started")
d.click_image(pic_url + 'queding@auto.png')
time.sleep(2)
......省略
#关闭队伍界面
d.click_image(pic_url + 'zy_180@auto.png', offset = (1, -0.5), safe = False)
time.sleep(2)
#一段对话
d.click(0.5, 0.5)
time.sleep(3)
#点击前往挑战
d.click_image(pic_url + 'zy_0@auto.png', offset = (-1.5, -1), safe = False)
time.sleep(5)
#停止指引
d.click(0.5, 0.5)
rp.close()

这段代码中,详细列举了新手的每一步操作,搜索图片,然后点击对应的位置
因为代码中所写的步骤都是固定的,所以面临两个问题

  • 引导修改
    如果游戏迭代过程中,对新手指引的步骤进行了修改,代码也需要同步修改

  • 中断
    如果代码执行过程中断了,继续执行的话,需要删减部分代码
    如果游戏内出现阻碍新手的弹框、提示之类的,也无法进行判断

所以我们需要一个更好的办法来解决通用和中断的问题

部分资料
Python 基础:http://www.runoob.com/python3/python3-tutorial.html
Poco 文档:http://poco.readthedocs.io/zh_CN/latest/
Poco 示例:http://poco.readthedocs.io/zh_CN/latest/source/README.html#tutorials-and-examples

修改过程

接下来的过程以另一个游戏为例,使用基于 UI 的方式来执行新手指引的过程

这里需要提前集成 Poco-SDK

我们先分析下整个新手过程中的通用部分,我们主要是把整个新手拆分成不同的 UI,方便我们进行不同的操作

  • 开场战斗
  • 角色创建
  • 存在指引手指的指引步骤
  • 剧情对话
  • 各类提示框

有了大致的 UI 后,我们可以考虑直接使用循环点击指引手指指引的位置,过程中存在其它界面时,就执行对应的操作,大致结构如下:

while(true):
    if 指引手指.exists():
        点击手指位置
    elif 剧情对话.exists():
        点击剧情对话
    elif 其他界面.exists():
        点击界面位置

上面的代码就不需要和最初那样,考虑新手指引的步骤;因为一直在检查当前界面显示的 UI,所以也不用考虑执行中断后重哪里继续
但是游戏过程中需要考虑到一个问题,在网络中断的时候,弹框会和指引手指或者其它界面一起出现;虽然其它 UI 会被提示框遮挡,但是 UI 还是存在于当前界面的;所以在判断的时候,需要考虑下编写的顺序

优化过程

在 Poco 的示例中有一个等待 UI 出现的方法

https://poco-chinese.readthedocs.io/zh_CN/latest/source/poco.pocofw.html#poco.pocofw.Poco.wait_for_any

在超时前返回等到的第一个 UI 代理对象,等不到 UI 时会报错
所以上面的代码可以改一下:

while(true):
    ui = poco.wait_for_any([指引手指,剧情对话,其他界面])
    if ui is 指引手指:
        点击手指位置
    elif ui is 剧情对话:
        点击剧情对话
    elif ui is 其他界面:
        点击界面位置

但是在等待过程中,一直没有找到 UI 时,会报错,我们可以在报错的时候做一些处理,比如停止循环

while(true):
    try:
        ui = poco.wait_for_any([指引手指,剧情对话,其他界面])
    except:
        break
    if ui is 指引手指:
        点击手指位置
    elif ui is 剧情对话:
        点击剧情对话
    elif ui is 其他界面:
        点击界面位置

最终代码

最终使用 Poco 跑新手流程的完整代码

# -*- encoding=utf8 -*-
__author__ = "HBoPRC"

from airtest.core.api import *
from poco.drivers.unity3d import UnityPoco
# 连接设备
connect_device("Android:///")
# 初始化poco
poco = UnityPoco()
# 定义一个变量来判断是否还在新手阶段
isGuide = True
while (isGuide):
    # 每次需要重新获取各个界面,以保证取到最新的信息
    # 剧情对话界面
    talkV = poco("剧情对话")
    # loading界面,这里主要是进出战斗耗时比较长,单独列出来
    loadW = poco("loading")
    # 创建界面
    roleS = poco("角色创建")
    # 指引手指
    finger = poco("指引手指")
    # 获得奖励界面,因为有战斗,结算界面没有手指,需要单独处理
    getA = poco("奖励界面")
    # 各类提示框,主要是断线那部分弹框
    popWin = poco("提示框")

    try:
        # 等待出现上面的界面,设置120s超时,默认就是120s,可以按实际情况修改
        # 这里写UI参数的时候需要注意下先后顺序,因为常见的情况:指引的时候可能会断线重连,弹出提示框,此时会同时存在指引手指和提示框,需要先处理提示框
        guideV = poco.wait_for_any([popWin, talkV, loadW, roleS, country, getA, finger], timeout = 120)
    except:
        # 没找到上面的界面,设置不在新手阶段,跳出循环
        isGuide = False
        break
    # 判断,根据出现的界面进行相关的操作
    if guideV is popWin:
        # 因为提示框的层级最高,我们需要优先判断处理,因为主要是考虑的断线重连情况,这里可以加一个休眠,保证跳过连接过程;或者是再单独判断一个转圈的UI
    elif guideV is talkV:
        # 如果是对话框,点击关闭
    elif guideV is loadW:
        # 这里单独处理loading是因为考虑到进入副本战斗的时候,loading过了,还需要先战斗一段时间才会进行指引提示;其实也可以把wait方法里的超时设长点
    elif guideV is roleS:
        # 角色创建界面,进行选择头像,性别,姓名之类的操作
    elif (guideV is getA):
        # 这里关闭掉结算奖励界面
    elif guideV is finger:
        # 指引手指,点击对应的位置就行了

完成以上步骤后,我们已经可以运行一个完整的新手流程了
接下来可以使用命令直接运行写好的 .air 脚本

http://airtest.readthedocs.io/zh_CN/latest/README_MORE.html#run-test-case

将命令保存为不同的批处理文件后,批量打开,就可以做到最简单的多台机器同时运行了,需要注意,如果使用命令的话,需要将脚本内连接设备的代码删掉

python -m airtest run xinshou.air --device Android://127.0.0.1:5037/手机序列号 --log \log\nexus4
python -m airtest report xinshou.air --log_root \log\nexus4  --outfile \log\nexus4\log.html --lang zh
pause
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 16 条回复 时间 点赞
啵板 #17 · 2019年07月31日 Author
在路上 回复

源码在这里:PocoAccelerationMixin

adolli 回复

请问 dismiss 的资料在哪里啊?没有搜到

仅楼主可见
仅楼主可见
啵板 回复

这方法可以哦。

啵板 #12 · 2018年08月03日 Author
怀沙 回复

对,底层都是查出坐标来操作,这些工具都是方便我们去寻找。

啵板 #11 · 2018年08月03日 Author
重来看雨 回复

我是打包的时候专门打一个线上带 poco-SDK 的包内部使用。

其实不管是基于坐标、图像、UI 对象;最终都是转换成绝对坐标的
比如 APP 上的角色头像
在固定分辨率下、坐标位置是固定的;你用图像识别这个头像然后也是转换位坐标位置;UI 对象也一样的

外放的就是预上线的包了吧,却无法使用自动化回归,那不是引起一个问题? 内部测试的包可以使用自动化方案,快速回归。但预上线的包,则没有自动化回归,得靠人工回归,这套自动化方案的效率是否打折购了?预上线的包质量也是否会打折购?

匿名 #8 · 2018年07月31日
重来看雨 回复

外放的包不要把 sdk 打进去就可以了,只有内部测试用的包才打 SDK

还是需要侵入 sdk,平时发包给渠道或者运营也是带 sdk 的包? 如果这样,不是使某些外挂工作室更方便出脚本?

陈子昂 回复

官方有个讨论 QQ 群:437119175

adolli 回复

学习了,看了下 dismiss,确实方便了很多

有没有 qq 啊。加我个 qq 728661182。有些公司和团体也会一起来讨论这块的。

匿名 #3 · 2018年07月30日

😋 😋 😋
超级赞!

思寒_seveniruby 将本帖设为了精华贴 07月28日 23:00

总结得很到位啊!有哪点哪的还可以用 poco.dismiss

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册