芭比哇玩123 发表于 2017-6-14 09:52:51

atx 对游戏分发包实践

本帖最后由 芭比哇玩123 于 2017-6-14 10:09 编辑

我的情况
很早之前就看到atx的开源,因为基于图片识别,所以眼前一亮,特别是进入游戏行业后(发行公司,勉强算游戏行业吧。。。。),因为游戏的UI控件,用uiautomator viewer是无法查看的,也就是说,游戏与appium,macaca等基本无缘。。之前用appium把 联运sdk(iOS,Android)的脚本都写好了。脚本中,对游戏内容的操作。。都是坐标点击。
https://testerhome.com/uploads/photo/2017/c2cb9463-e316-4273-ae30-6388051d0bb3.png!large
都是sdk内容case,对支付因为需要进入到游戏里,还要新手剧情等等。所以都是人工测支付。废话不多说了。
最近上线的游戏有几个。游戏分包后(最少都20+的渠道。。😂 )所以需要时间比较多,总监说,能自动化的都自动化吧。。然后就开始了(当然也看过其他开源的东东,比如腾讯那个GAutomator,testin的cocos-plugin,但都是需要cp接入的。不适合)

开始搞起
恩,atx环境的东东就不多说了。遇到坑就爬吧!
https://testerhome.com/uploads/photo/2017/4f499321-afd8-4b15-a165-c7cf70c021c9.png!large
这个是我的项目结构图,说明一下
run.py
run.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 是一些公共的方法,比如获取屏幕像素,断言方法(失败截图),查找元素等,图片操作等

<p> </p>
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.split('x')
    size= ),int(size)] #size 为width size 为high
    return sizedef 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这里两个,等待图片出现,一个是 点击图片
<p>def tap(self,driver,value):
    self.driver = driver
    size = self.size()
    return self.driver.click(size * value, size * value)</p><p> </p>
点击坐标的方法,比例点击
testcase
case如下

<p> </p>
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')点击公告
https://testerhome.com/uploads/photo/2017/db9857b4-ba5c-444c-a7fc-71be1e4e8da2.png!large

点击微信logo自动登录
https://testerhome.com/uploads/photo/2017/51ebe31d-c00e-4399-9fa8-f8f805b8b156.png!large

然后进入游戏
https://testerhome.com/uploads/photo/2017/495bd59b-01e8-4bf2-bcd5-e98cba0aaaa2.png!large

点击砖石

https://testerhome.com/uploads/photo/2017/2d74db82-f977-45e0-a99d-47a77a69815c.png!large

点击6元的
https://testerhome.com/uploads/photo/2017/0bf2aae6-df66-4254-b33f-c301e3d124db.png!large

然后弹出ysdk的支付界面
https://testerhome.com/uploads/photo/2017/8c71f15a-60f1-4615-9d50-1ab8f89ec5ad.png!large

这里界面可以用 控件去操作了。点击微信支付,弹出微信支付界面
https://testerhome.com/uploads/photo/2017/8d96d254-22da-4c06-9d7b-f86f41a77ad1.png!large

这里原生控件无法识别,所以就断言该图片是否为空
https://testerhome.com/uploads/photo/2017/7e27bd21-dd9e-4d76-ae44-315d186ddb66.png!large

取消支付,这里选择点击返回键,回到 ysdk的支付界面,断言 ‘确认支付方式’的text.
case 结束。报告如下
https://testerhome.com/uploads/photo/2017/dd2adeef-2579-4da6-9a52-88a9ce8924fa.png!large

关于并行
貌似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.text)
a.click()上面的ysdk 支付界面, Q币用text无法识别,但driver.source() 显示的 text就是 Q币。而且那几个支付方式排序是会变的。因此无法直接使用索引值。最后使用这段代码。
3,还是元素问题,使用appium习惯了,总是用uiautomator viewer 查看元素,但uiautomator viewer 能显示的元素(非webview),atx不一定能识别。出现这种问题时
多使用 driver.source() 打印一下。


测试的味道 发表于 2017-6-14 15:32:53

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

小爸爸 发表于 2017-6-14 15:33:33

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

小皮球的故事 发表于 2017-6-14 15:35:23

登录就送夏一鸽的游戏。

芭比哇玩123 发表于 2017-6-14 15:38:25

小皮球的故事 发表于 2017-6-14 15:35
登录就送夏一鸽的游戏。

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

小皮球的故事 发表于 2017-6-14 15:39:03

芭比哇玩123 发表于 2017-6-14 15:38
66666,话说。。我哪里暴露了

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

小爸爸 发表于 2017-6-14 15:41:22

报错:‘空的测试套件‘’,执行函数命名:"test_cameraCase " ; Pycharm 下文件夹、文件、类、函数名称,不能重复; 执行函数名test_

Pycharm 对命名检查很严格;
页: [1]
查看完整版本: atx 对游戏分发包实践