Airtest框架和Poco框架常见问题

Posted 测试界的飘柔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Airtest框架和Poco框架常见问题相关的知识,希望对你有一定的参考价值。

Airtest

报告可以导出发给别人看吗

Airtest的报告是可以打包发给别人看的。

① 想要导出报告发给别人观看,我们需要生成报告的命令中传入 --export 参数,这样就可以将 包含静态资源文件和图片文件的报告 导出到一个指定的文件夹内,之后直接将整个文件夹发送给别人观看即可。

② 如果生成报告时不传入 --export 参数,那么报告中的静态资源文件和图片文件将使用 绝对路径 来访问,此时将整个文件夹发给别人观看,别人也是无法正常观看的。

如何在报告中显示报错信息

可以使用如下代码:

import traceback
try:
    xxxx
except:
     log("出错啦", traceback. format_exc())

如何取消脚本执行过程刷新大量的log信息

在脚本运行的时候, Airtest 默认会刷新很多log信息,如下图所示:

如果你不想这些log信息干扰你提取有效的报错信息时,你可以在脚本代码开头加上log级别的设定:

# -*- encoding=utf8 -*-
__author__ = "user"
import logging
logger = logging.getLogger("airtest")
logger.setLevel(logging.ERROR)
from airtest.core.api import *
auto_setup(__file__)

这样运行时只会在初始化手机时会有少量log输出,初始化完毕后就能够对logger进行过滤了。

用Airtest测试ios一定要用macOS吗

① 使用 xcode 部署 iOS-Tagent或WDA 需要在macOS完成

② 部署好以后可以在macOS或Windows机器上连接到iOS手机进行测试

如何删除iOS输入框的内容

① 对于android平台,我们可以使用多种方法来删除输入框的内容,比如使用 keyevent 接口:keyevent(“KEYCODE_DEL”) ;或者使用Poco的 set_text() 方法:poco(“xxx”).set_text(“”) ;

②但对于iOS平台来说,暂不支持 set_text() 接口,也不支持 keyevent(“KEYCODE_DEL”) ,所以这俩种方法对于iOS的输入框来说是无效的。而iOS是支持 text() 方法的,所以我们可以用 text(“\\b”,False) (每次删除1个字符,删除后不执行 enter ),来实现iOS输入框内容的删除。

自定义截图压缩精度

① 在本地运行脚本时自定义:

# quality取值[1,99],airtest默认取10,希望获得更高精度可以取值75
airtest run xxx --compress 75

② 在脚本中自定义(优先级高于在命令行自定义):
自定义全局的截图压缩精度:

import airtest.core.api import *
ST.SNAPSHOT_QUALITY = xxx

自定义单张截图的压缩精度:

snapshot(quality=my_quality)

Airtest支持多设备运行吗

输入密码时Airtest不显示密码键盘

脚本运行到输入密码时,手机弹出了安全键盘,但是在Airtest中却不显示这个安全键盘,这是为什么?

其实这个是正常现象,Airtest不会录制有安全键盘的画面,但是各种输入操作还是正常进行的。

Airtest如何局部截图、找图

Airtest局部截图+找图、截屏另存为

Airtest的多图查找

Airtest没有提供专门的API给我们进行多图查找,但是我们可以用简单的语法来实现:

picList = [pic1,pic2,pic3]  # 截图的图片对象列表
for pic in picList:
     pos = exists(pic)
     if pos:
         touch(pos)
         break  # 只要找到图片列表中的任何一张图片,就执行touch

invalid syntax

典型的python语法错误,常见于脚本未换行、缩进错误、缺少 " 或者 ( 等。

怎么点击软键盘上面的搜索?输入后回车?

如需点击软键盘上面的 search 按钮,则:

text("文本",enter=False,search=True)

同理,如需输入后回车,则 text(“文本”,enter=True) ,不需要回车则, text(“文本”,enter=False) 。

不能输入密码?密码界面黑屏?

应用的登录页面或者密码界面一般都不给截屏录屏的,同学们可以检查看看你的测试设备的设置里面,有没有安全键盘、防止恶意截屏录屏之类的设置,关掉就行。

如果是App本身做的安全机制,需要让开发打一个测试的包看能不能去掉。

‘NoneType’ object has no attribute xxxx

这个报错可能出现在不同的方法里面,比如:

AttributeError: 'NoneType' object has no attribute 'snapshot'

AttributeError: 'NoneType' object has no attribute 'start_app'

出现这些报错,基本上都是因为同学们在脚本中没有连接设备,所以只要在脚本开头,补充上连接设备的脚本即可。

Poco

poco无限重启的解决办法

如果开了网络代理的话,需要先 关闭各种代理和VPN ,否则可能会影响到poco通讯

检查手机助手内是否对 pocoservice.apk 做了限制,例如在某版本的华为手机中需要开启 允许自启动 和 允许后台活动。

这里我们列举几个常见手机品牌的设置方式:

华为:手机管家-应用启动管理-PocoService.apk-手动管理,允许自启动开启,允许后台活动开启

OPPO:设置-电池-应用耗电管理-PocoService.apk-允许应用自启动,允许完全后台行为

VIVO:电池-后台高耗电-> PocoService 开启

一加:设置-电池优化-PocoService-不优化

当然,不同手机品牌,甚至同品牌不同型号手机的配置方式,都有可能不大一样,同学们要自己查找手机里面与 电池优化 和 后台活跃 相关的设置即可,保证给 pocoservice.apk 足够的活跃权限且不被电池优化行为干掉。

不能和uiautomator同时启动,否则会相互冲突

可以尝试 重启手机 / 重装pocoservice 看看是否会恢复

poco拿不到控件,poco的支持情况

目前只有原生应用可以直接使用poco(无需接入pocoSDK),非原生应用,比如各种游戏应用、H5小程序、混合开发的应用等,都不能直接使用poco拿取控件,绝大多数的游戏支持接入对应引擎的pocoSDK之后,可以获取控件信息。

poco定位报错找不到

有时候IDE自动生成的poco定位脚本会非常长,层级也非常深,回放时可能出现找不到控件的情况;这时不建议同学们直接使用自动生成的定位脚本,可以根据UI树详情,另外编写更精简的定位脚本,推荐使用正则匹配来进行脚本定位会好一些。

如果定位脚本是同学们自己编写的,请检查对应的属性或者层级关系,看是否是脚本错误而导致找不到元素。

如出现单独选中调试脚本,可以找到控件,但实际运行脚本又容易找不到控件,建议在该条定位脚本之前添加足够的 sleep() ,确保画面跳转稳定后,再来查找控件。

“远程主机强迫关闭了一个现有的连接”、“socket connection broken”

很多新手在使用poco的时候,都容易把连接设备、初始化poco和打开应用的脚本顺序搞乱,导致出现一大堆奇妙的报错。
比如大家最常见的:“远程主机强迫关闭了一个现有的连接”、“socket connection broken” 等等。
最正确的顺序是:先连接设备(一般在 auto_setup 接口里面连接)–> 再打开应用(一般用 start_app 接口)–> 等应用开启完毕,最后才初始化 poco 。

# -*- encoding=utf8 -*-
__author__ = "Airtest"
from airtest.core.api import *

# 连接设备
auto_setup(__file__,logdir=True,devices=["Android://127.0.0.1:5037/emulator-5554?cap_method=JAVACAP"])

#启动应用
start_app("com.NetEase")
sleep(6.0)

# 初始化poco
from poco.drivers.unity3d import UnityPoco
poco = UnityPoco()

poco("btn_start").click()

所以同学们在初始化poco的时候,千万记住要检查下这几条代码的顺序,避免产生不必要的报错。

同一个脚本初始化多个poco(1个原生1个游戏)

在同一个脚本内,支持初始化多个不一样的poco,比如 Android poco 和 unity poco,就是可以共存的:

from airtest.core.api import *

auto_setup(__file__,devices=["android://127.0.0.1:5037/emulator-5554?cap_method=MINICAP_STREAM&&ori_method=MINICAPORI&&touch_method=MINITOUCH"])

from poco.drivers.android.uiautomation import AndroidUiautomationPoco
A_poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)

sleep(1.0)

A_poco("poco").click()
sleep(2.0)

from poco.drivers.unity3d import UnityPoco
U_poco = UnityPoco()

U_poco("btn_start").click()

但是我们不能在同一个脚本内,反复初始化相同的poco,比如多次初始化Android poco,就有可能导致奇奇怪怪的错误出现。


资源分享

下方这份完整的软件测试视频学习教程已经上传CSDN官方认证的二维码,朋友们如果需要可以自行免费领取 【保证100%免费】

airtest图像识别+poco使用实践

airtest图像识别+poco使用实践

前言

最近在学习UI自动化,关于框架的挑选对我来说没啥好挑的,就airtest了,图像识别和接入poco-SDK,公司项目还没有接好SDK,拿网易的提供的demo先来练练手。

安装

我习惯使用python来跑自动化,有些库得安装下 1、airtest

pip install airtest

2、UI自动化框架:poco

pip install poco

3、测试框架:pocounit

pip install pocounit

4、Androiddemo下载链接:[1]

先上官网的例子

一、父类

from poco.drivers.unity3d import UnityPocofrom pocounit.case import PocoTestCasefrom pocounit.addons.poco.action_tracking import ActionTrackerclass MyBaseTestCase(PocoTestCase): @classmethod def setUpClass(cls): super(MyBaseTestCase, cls).setUpClass() cls.poco = UnityPoco() # 启用动作捕捉(action tracker) action_tracker = ActionTracker(cls.poco) cls.register_addon(action_tracker)

二、测试用例(继承父类)

# 一个文件里建议就只有一个TestCase# 一个Case做的事情尽量简单,不要把一大串操作都放到一起class MyTestCase(MyBaseTestCase): def setUp(self): # 可以调用一些前置条件指令和预处理指令 pass # 函数名就是这个,用其他名字无效 def runTest(self): '''self.assertEqual相关的断言方法不会收集在测试报告中(网易那边不支持) 要用self.call_airtest.airtest_api.assert_equal相关方法才会收集在测试报告中 ''' # 普通语句跟原来一样 # self.poco(text='角色').click() # 断言语句跟python unittest写法一模一样 # self.assertTrue(self.poco(text='最大生命').wait(3).exists(), "看到了最大生命") # self.poco('btn_close').click() # self.poco('movetouch_panel').offspring('point_img').swipe('up') start_btn = self.poco('btn_start') start_btn.click() time.sleep(0.5) self.assertEqual(start_btn.get_name(),'btn_start',msg='测试一下断言报告') try: self.assertEqual(start_btn.get_name(),'btn_start1',msg='测试一下断言报告 1') except :pass self.try_assert_mothod(self.assertEqual,start_btn.get_name(), 'btn_start1','测试一下断言报告 2') basic_btn = self.poco(text='basic') basic_btn.click([0.5,0.5]) self.try_assert_mothod(self.assertEqual,basic_btn.get_name(),'abc','测试一下断言报告 3') back_button= self.poco('btn_back',type='Button').focus([1,0.1]) back_button.click() if self.poco(text='drag drop').exists(): self.poco(text='drag drop').click() else: back_button.click() def tearDown(self): # 如果没有清场操作,这个函数就不用写出来 pass # 不要写以test开头的函数,除非你知道会发生什么 # def test_xxx(): # passif __name__ in '__main__': pocounit.main() # generate html report time_str = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") simple_report(__file__,output=REPORTPATH/f'test_{time_str}.html',logpath=str(LOGPATH))

实践poco+图像识别

一、封装断言方法

如果正常执行断言方法,失败的话后面的用例会被中断,所以得加上异常兼容;

1、poco的封装

 def poco_assert_mothod(self,func,*args,case_msg=None): ''' :param func: 断言方法 :param args: 断言方法里需要的参数 :return: 返回断言结果 ''' try: if case_msg: return func(*args,msg=case_msg) else: return func(*args) except Exception as e: print(f'{func} ERROR:', e) return False

2、airtest图像识别的封装

 def img_assert_mothod(self,func=None,img_1=None,img_2=None,case_msg='空白说明',threshold=0.8,target_pos=False,rgb=False): ''' :param func: 断言方法,用加括号 :param args: 断言方法里需要的参数 :return: 返回断言结果 ''' ''' Template(r"tpl1532588127987.png", record_pos=(0.779, 0.382), resolution=(407, 264), threshold=0.6, target_pos=5, rgb=False) threshold:识别精准度 target_pos:点击坐标偏移 rgb:色彩识别 示例:assert_not_exists(self.img_template(img), msg) ''' def img_template(img): if not target_pos: return Template(self.img_path + img, threshold=threshold, resolution=self.screen_size, rgb=rgb) else: return Template(self.img_path + img, threshold=threshold, target_pos=target_pos, resolution=self.screen_size, rgb=rgb) try: if case_msg and img_1 and img_2: return func(img_template(img_1),img_template(img_2),case_msg) elif case_msg and (img_1 or img_2): if img_1: return func(img_template(img_1),case_msg) elif img_2: return func(img_template(img_2),case_msg) elif case_msg is False: return func(img_template(img_1)) except Exception as e: print(f'{func} ERROR:', e)

二、给父类添加ADB的方法

class MyBaseTestCase(PocoTestCase): @classmethod def setUpClass(cls): super(MyBaseTestCase, cls).setUpClass() cls.poco = UnityPoco() # 启用动作捕捉(action tracker) action_tracker = ActionTracker(cls.poco) cls.register_addon(action_tracker) self.img_ath = None # AdbShell在之前的的文章里可以找到,封装常用的adb命令 self.adb = AdbShell() self.task_id = None if self.check_devices(): self.screen_size = self.adb.get_screen_size() # demo游戏包名 self.app_name = 'com.NetEase' self.app = self.adb.get_thirdparty_app(filter=self.app_name) else:raise ConnectionError # 获取操作系统 self.system = os.name def check_devices(self): if self.adb.getDevices() is not False: return True else: self.adb.adb('adb kill-server') self.adb.adb('adb start-server')

三、poco常用方法

 def __doc__(self): '''选择UI对象''' self.poco('') # select by node name self.poco('bg_mission') # select by name and other properties self.poco('bg_mission', type='Button') self.poco(textMatches='^据点.*$', type='Button', enable=True) #加上对应属性来定位 # select by direct child/offspring self.poco('main_node').child('list_item').offspring('item') # 顺序选择 items = self.poco('main_node').child('list_item').offspring('item') print(items[0].child('material_name').get_text()) # 迭代遍历一组UI items = self.poco('main_node').child('list_item').offspring('item') for item in items: item.child('icn_item') '''读取属性''' mission_btn = self.poco('bg_mission') print(mission_btn.attr('type')) # 'Button' print(mission_btn.get_text()) # '获取text' print(mission_btn.attr('text')) # equivalent to .get_text() print(mission_btn.exists()) # True/False, exists in the screen or not '''操作UI对象''' # 点击 self.poco('bg_mission').click() self.poco('bg_mission').click('center') self.poco('bg_mission').click([0.5, 0.5]) # equivalent to center self.poco('bg_mission').focus([0.5, 0.5]).click() # equivalent to above expression self.poco() # 滑动 joystick = self.poco('movetouch_panel').child('point_img') joystick.swipe('up') joystick.swipe([0.2, -0.2]) # swipe sqrt(0.08) unit distance at 45 degree angle up-and-right joystick.swipe([0.2, -0.2], duration=0.5) # 拖拽 self.poco(text='突破芯片').drag_to(self.poco(text='岩石司康饼')) # 等待 self.poco('bg_mission').wait(5).click() # wait 5 seconds at most,click once the object appears self.poco('bg_mission').wait(5).exists() # wait 5 seconds at most,return Exists or Not Exists self.poco().wait_for_appearance() # 等待目标出现,超时raises PocoTargetTimeout self.poco().wait_for_disappearance() # 等待目标消失,超时raises PocoTargetTimeout # self.poco.wait_for_any() # self.poco.wait_for_all() '''全局操作''' # 点击 self.poco.click([0.5, 0.5]) # click the center of screen self.poco.long_click([0.5, 0.5], duration=3) # swipe from A to B point_a = [0.1, 0.1] center = [0.5, 0.5] self.poco.swipe(point_a, center) # 滑动 direction = [0.1, 0] self.poco.swipe(point_a, direction=direction) # 截屏 from base64 import b64decode b64img, fmt = self.poco.snapshot(width=720) open('screen.{}'.format(fmt), 'wb').write(b64decode(b64img))

四、商店测试用例

SDK还没有接好,目前是用图像识别的方式写个demo

class TestShop(MyBaseTestCase): ''' 用例设计参考思维导图的测试用例 ''' def setUp(self): self.call_airtest.img_path = Path(__file__).parent / 'imgFolder' def close_tips(self): tips_1 = self.call_airtest.img_assert_mothod(exists,'goods_tips_close_btn.png',case_msg=False) tips_2 = self.call_airtest.img_assert_mothod(exists,'rank_close_btn.png',target_pos=3,case_msg=False) if tips_1: touch(tips_1) if tips_2: touch(tips_2) def runTest(self): self.close_tips() '''用例执行方法在这里''' self.entrance('shop_icon_1.png', 'shop_icon_2.png', 'shop_homepage.png') self.shop_type('yuanbao_shop.png', 'duihuan_shop.png','jipin_shop.png','jifen_shop.png') self.title('shop_tag_1.png','shop_tag_2.png','shop_tag_3.png') # 购买多个商品,图片坐标偏移8 shop_goods_list = [ ('银两','yinliang.png','yinliang_max.png',5), ('福袋碎片','fudai.png','fudai_max.png',1), ('5级宝石', 'baoshi5.png', 'baoshi5_max.png', 5), ('兽丹礼包', 'shoudan.png', 'shoudan_max.png', 5), ] for test_data in shop_goods_list: self.gold_daily_shop(*test_data) def tearDown(self): # 如果没有清场操作,这个函数就不用写出来 pass # 判断入口 def entrance(self,enter_img_1=None,enter_img_2=None,shop_homepage=None): shop_icon = self.call_airtest.img_assert_mothod(assert_exists,enter_img_1,case_msg='显示商店图标') if shop_icon: touch(shop_icon) sleep(1.5) self.call_airtest.img_assert_mothod(assert_exists,shop_homepage,case_msg='进入后出现商品页签') else: wait_shop_icon = wait(Template(self.call_airtest.img_path+enter_img_2),5) if wait_shop_icon: touch(wait_shop_icon) self.call_airtest.img_assert_mothod(assert_exists,shop_homepage,case_msg='进入后出现商品主页') else: raise TargetNotFoundError def shop_type(self,type_1,type_2,type_3,type_4): '''各个商城''' self.shop_type_1 = self.call_airtest.img_assert_mothod(assert_exists,type_1,case_msg='显示元宝商城') self.shop_type_2 = self.call_airtest.img_assert_mothod(assert_exists,type_2,case_msg='显示兑换商城') self.shop_type_3 = self.call_airtest.img_assert_mothod(assert_exists,type_3,case_msg='显示极品商城') self.shop_type_4 = self.call_airtest.img_assert_mothod(assert_exists,type_4,case_msg='显示积分商城') def title(self,shop_tag_1,shop_tag_2,shop_tag_3): '''页签''' self.shop_tag_1 = self.call_airtest.img_assert_mothod(assert_exists,shop_tag_1,case_msg='显示每日特惠页签') self.shop_tag_2 = self.call_airtest.img_assert_mothod(assert_exists,shop_tag_2,case_msg='显示常用道具页签') self.shop_tag_3 = self.call_airtest.img_assert_mothod(assert_exists,shop_tag_3,case_msg='显示特权商城页签') def gold_daily_shop(self,goods,goods_img,goods_max_img,buy_max): '''商店购买''' # 点击元宝商城 if self.shop_type_1: touch(self.shop_type_1) # 点击每日特惠 if self.shop_tag_1: touch(self.shop_tag_1) else: print('入口目标没有找到,调整截图或降低识别率') raise TargetNotFoundError # self.call_airtest.img_assert_mothod(assert_exists,cost_img,case_msg=f'{goods}道具价格') # 商品 self.goods_pos = self.call_airtest.img_assert_mothod(assert_exists,goods_img,target_pos=8,case_msg=f'显示{goods}道具') # swipe(self.goods_pos,vector=(0.5,0.8)) if self.goods_pos: # 限购 for i in range(buy_max): touch(self.goods_pos) # 滑动到底部 # 滑动一次接近翻一页 self.swipe_up_pos = (520,1910),(520,607) swipe(*self.swipe_up_pos) swipe(*self.swipe_up_pos) sleep(1) self.goods_max_pos = self.call_airtest.img_assert_mothod(assert_exists,goods_max_img,case_msg=f'{goods}购买{buy_max}次后,达到上限灰化') # 划回去 self.swipe_down_pos = (520,607),(520,1910) swipe(*self.swipe_down_pos) swipe(*self.swipe_down_pos) sleep(1)

结语

UI自动化这块我接触的少,经验也不足,属于摸索起步阶段,据了解目前多数同行都应用于回归测试,向他们请教怎么应用,后续有好的应用方法再更新上来,有好的建议欢迎留言,感谢。

微信二维码

引用链接

[1] 下载链接:: http://top.gdl.netease.com/poco-res/poco-demo-unity-game-android.zip


以上是关于Airtest框架和Poco框架常见问题的主要内容,如果未能解决你的问题,请参考以下文章

AirtestProject测试框架

AirtestProject测试框架

AirtestIDE实践二:Poco框架试用

UI 自动化找元素太难?AIRtest 框架你值得拥有!

Airtest基于图像识别的自动化测试工具

Airtest基于图像识别的自动化测试工具