ATX atx 对游戏分发包实践

重来看雨 · 2017年06月13日 · 最后由 重来看雨 回复于 2020年09月07日 · 4284 次阅读
本帖已被设为精华帖!

我的情况

很早之前就看到 atx 的开源,因为基于图片识别,所以眼前一亮,特别是进入游戏行业后(发行公司,勉强算游戏行业吧。。。。),因为游戏的 UI 控件,用 uiautomator viewer 是无法查看的,也就是说,游戏与 appium,macaca 等基本无缘。。之前用 appium 把 联运 sdk(iOS,Android)的脚本都写好了。脚本中,对游戏内容的操作。。都是坐标点击。

都是 sdk 内容 case,对支付因为需要进入到游戏里,还要新手剧情等等。所以都是人工测支付。废话不多说了。
最近上线的游戏有几个。游戏分包后(最少都 20+ 的渠道。。😂 )所以需要时间比较多,总监说,能自动化的都自动化吧。。然后就开始了(当然也看过其他开源的东东,比如腾讯那个 GAutomator,testin 的 cocos-plugin,但都是需要 cp 接入的。不适合)

开始搞起

恩,atx 环境的东东就不多说了。遇到坑就爬吧!

这个是我的项目结构图,说明一下

run_case.py

run_case.py 是跑脚本得,配置好 test case 路径去搜索 py 文件,然后把 test case 加入到测试容器

casepath = './/'+configure.game_name+'_TestCase'
discover = unittest.defaultTestLoader.discover(casepath, pattern='test0*', top_level_dir=None)

result 文件夹与 tmcs_images

result 文件夹是存放测试报告与截图的
tmcs_images 与 tmcs_TestCase 都是存放 图片的,为什么会有两个?。。一个是 tmcs_TestCase 做 unittest 图片相对路径,一个 runcase.py 的相对路径

configure.py

configure.py 是配置信息的,game_name 是用来区分游戏的。主要作用是 在于 图片存放的路径和 搜索 case 的路径

game_name = 'tmcs'
device_name = '4c14b8b2'
package_name = 'com.tencent.tmgp.shardsofdestiny'
activity_name = 'com.zhankaigame.destiny.RenWanTangMainActivity'

public.methods.py

public.methods.py 是一些公共的方法,比如获取屏幕像素,断言方法(失败截图),查找元素等,图片操作等

def size(self):
    if configure.device_name == '':
        size = 'adb shell wm size'
    else:
        size = 'adb -s %s shell wm size' % configure.device_name
    a =  os.popen(size)
    for i in a:
        pass
    size =i.split(': ')
    size = size[1].split('x')
    size= [int(size[0]),int(size[1])] #size[0] 为width size[1] 为high
    return size
def dy(self,driver,value1,value2,screen_name):
    self.driver = driver
    try:
        self.assertEqual(value1, value2)
    except:
        self.screencap(self.driver,name=screen_name)
        self.assertEqual(value1, value2)
def dy_IsNone(self,driver,obj,screen_name):
    self.driver = driver
    try:
        self.assertIsNone(obj)
    except:
        self.screencap(self.driver,name=screen_name)
        self.assertIsNone(obj)
def dy_IsNotNone(self,driver,obj,screen_name):
    self.driver = driver
    try:
        self.assertIsNotNone(obj)
    except:
        self.screencap(self.driver,name=screen_name)
        self.assertIsNotNone(obj)
def element(self, driver, methods, value):
    '''
    :param driver:驱动
    :param methods: 方式
    :param value: 值
    :return: 返回对象
    '''
    self.driver = driver
    if methods == 'text':
        return self.driver(text=value)
    elif methods == 'xpath':
        return self.driver(xpath=value)
    elif methods == 'resourceId':
        return self.driver(resourceId=value)
    elif methods == 'className':
        return self.driver(className = value)

def element_or_none(self, driver, methods, value):
    '''
    :param driver:驱动
    :param methods:方式
    :param value:知
    :return:元素存在返回元素,不存在,返回None
    '''
    self.driver = driver
    try:
        element = self.element(self.driver, methods, value)
        if len(element) == 0:
            self.assertEqual(True, False)
    except:
        element = None
    return element

因为 atx 查找元素后,是一个 list,如果 list 为空,也不报错的。。因此写了个方法,return None,主要用来做断言的

def images_or_none(self,driver,images_name,timeout = 10):
    self.driver = driver
    game_name = configure.game_name
    try:
        self.wait_images(self.driver, images_name,timeout)
    except:
        pass
    images =  self.driver.exists('./'+game_name+'_images/'+images_name)
    return images

这里用到 driver.wait() 和 driver.exists() 这里的图片相对路径,如果图片不存在,return None,主要用来做断言的

def click_images(self, driver,  images_name):
    self.driver = driver
    game_name = configure.game_name
    images = self.driver.click_image('./' + game_name +'_images/'+ images_name)
    return images

def wait_images(self,driver,images,timeout = 10):
    self.driver = driver
    game_name = configure.game_name
    images= self.driver.wait('./' + game_name + '_images/' + images,timeout)
    return images

这里两个,等待图片出现,一个是 点击图片

def tap(self,driver,value):
    self.driver = driver
    size = self.size()
    return self.driver.click(size[0] * value[0], size[1] * value[1])

点击坐标的方法,比例点击

testcase

case 如下

class YSDKSDK(unittest.TestCase,public.Methods):
    u'''YSDKC-sdk测试'''
    def setUp(self):
        if configure.device_name == '':
            self.driver = atx.connect()
        else:
            self.driver = atx.connect(configure.device_name)
        self.driver.start_app(configure.package_name,configure.activity_name)
        sleep(25)
    def tearDown(self):
        pass
        self.driver.stop_app(configure.package_name)
    def test_013(self):
        u'''调起微信支付,并取消'''
        self.click_images(self.driver, 'game-gonggao@auto.png')
        self.click_images(self.driver, 'game-weixin-login@auto.png')
        sleep(5)
        self.enter_game(self.driver)
        self.pay(self.driver)
        self.element(self.driver,'text','微信支付').click()
        images = self.images_or_none(self.driver,'weixinpay-view@auto.png',timeout=20)
        self.dy_IsNotNone(self.driver,images,'test_013')
        sleep(5)
        self.driver.keyevent('KEYCODE_BACK')
        sleep(5)
        ysdk_pay = self.element(self.driver, 'text', '确认支付方式')
        self.dy(self.driver, unicode(ysdk_pay.text), u'确认支付方式', 'test_013')

点击公告
点击微信 logo 自动登录
然后进入游戏
点击砖石
点击 6 元的
然后弹出 ysdk 的支付界面
这里界面可以用 控件去操作了。点击微信支付,弹出微信支付界面
这里原生控件无法识别,所以就断言该图片是否为空
取消支付,这里选择点击返回键,回到 ysdk 的支付界面,断言 ‘确认支付方式’ 的 text.
case 结束。报告如下

关于并行

貌似 atx 并行好容易,不需要设置端口什么的了。如我的项目结构,安装不同的渠道包后,然后就进入不同的渠道项目,配置好信息,Python runcase.py 就可以了

经验

1,对于无法识别的元素控件,如果使用图片操作的话,尽量不要 截文字的图,因为在不同的 rom,虽说都是 标准字体,但也是不一样的,造成脚本兼容性问题
2,atx 识别控件的话,建议使用 class name 和 id,text 不一定能识别到

b = []
a = self.element(self.driver,'className','android.widget.TextView')
for i in range(len(a)):
    b.append(a[i].text)
a[b.index(u'Q币')].click()

上面的 ysdk 支付界面, Q 币用 text 无法识别,但 driver.source() 显示的 text 就是 Q 币。而且那几个支付方式排序是会变的。因此无法直接使用索引值。最后使用这段代码。
3,还是元素问题,使用 appium 习惯了,总是用 uiautomator viewer 查看元素,但 uiautomator viewer 能显示的元素(非 webview),atx 不一定能识别。出现这种问题时
多使用 driver.source() 打印一下。
最后还是要感谢 @codeskyblue 开源

共收到 29 条回复 时间 点赞

不错~ 好好看看

登录就送夏一鸽的游戏。

蓝畔湖光 回复

66666,话说。。我哪里暴露了😂

思寒_seveniruby 将本帖设为了精华贴 06月13日 18:33

18 条 case 跑了 21 分钟,效率还是偏低了些

可以,不错。感谢楼主分享,好好研究研究。

要考虑封装,之后图片多了效率更低,图像识别的自动化测试以后维护也是个大坑,我之前就吃过亏,所以要尽可能的把图片和业务分离

重来看雨 回复

package_name 暴露的

因为要过新手剧情,剧情时间太长,已经向 cp 提需求,增加跳过剧情

terrychow 回复

是的,图片与业务分离,之前用 appium 做 app ui 自动化的时候就处理了(数据与代码分离)。。但这次的游戏分包,一个渠道包,其实就 20-30 条 case,游戏的图片共用,渠道 sdk 原则上不使用图片。维护应该难度问题不大。

雷子 回复

谢谢

天琴圣域 回复

应用宝的 package_name 这么复杂的 name 都能看出来。。厉害厉害

重来看雨 回复

😂 你没暴露,我就是之前在群里说句话的那个人

重来看雨 回复

com.tencent.tmgp.shardsofdestiny,shardsofdestiny 百度搜一下就知道了呀😁

天琴圣域 回复

还真是

不错不错 准备发给我司发行部门 QA 看看 😀

codeskyblue ATX 资料快速索引 中提及了此贴 06月21日 19:45

请问 “CP 接入” 是什么意思😀

孤云 回复

游戏开发商接入

此工具功能好强大,移动端自动化测试福音啊

重来看雨 回复

谢谢😄

重来看雨 atx 对游戏分发包实践 2.0 中提及了此贴 08月09日 13:24

获取的结果图片原先是在哪里?修改了底层放置到你指定的位置吗?

IAmTester 回复

获取结果?

mark,感谢分享

重来看雨 回复

就是匹配后的图片放在哪里,看了底层好像没写出匹配的图片,改了下可以写出

IAmTester 回复

匹配的图片,没有存起来

simple 专栏文章:[精华帖] 社区历年精华帖分类归总 中提及了此贴 12月13日 20:49
simple [精彩盘点] TesterHome 社区 2018 年 度精华帖 中提及了此贴 01月07日 12:08
wolfgao 移动客户端 /UI 开源测试框架梳理和大比拼 中提及了此贴 02月27日 21:03

你好,最近我们公司有游戏接入 sdk 的需求,作为自动化测试,我需要自动化测试 sdk, 可以加你微信进行沟通吗,我的是 kimihasukida,谢谢

如何通过图片点击尼,传图片名字报错如下

图片识别的精度太差了吧,尝试了几次图片都点击到了别的地方😂

是否精准,与截图有关,现在应该可以使用 https://github.com/AirtestProject,资料应该更多

重来看雨 如何提高游戏发行方的测试效率 中提及了此贴 02月02日 15:43
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册