文章目录
-
-
- 一、Airtest官网
- 二、Poco-SDK下载
- 三、中文文档
- 四、Airtest 常用函数
-
- 1 获取屏幕尺寸
- 2 引用其他air脚本
- 3 元素定位
- 4 点击元素身上的某一点:focus、click
- 5 等待元素出现或消失:wait_for_appearance、wait_for_disappearance
- 6 拖动:drag_to
- 7 滑动:swipe
- 8 连续滑动:swipe_along
- 9 按住n秒拖动: hold、to
- 10 长按:long_click
- 11 判断元素是否存在:exists
- 12 UI状态清除:invalidate
- 13 两指挤压收缩操作:pinch
- 14 无效的操作异常:InvalidOperationException
- 15 节点或属性不存在异常:PocoNoSuchNodeException
- 16 等待超时异常:PocoTargetTimeout
- 17 对象已被清除:PocoTargetRemovedException
- 五、自定义poco属性
-
Airtest Project
是网易游戏内部工具团队开发并开源的一款UI自动化测试工具。
一、Airtest官网
二、Poco-SDK下载
https://github.com/AirtestProject/Poco-SDK
三、中文文档
https://airtest.doc.io.netease.com/#
四、Airtest 常用函数
1 获取屏幕尺寸
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
android_poco= AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
screen_with, screen_height = android_poco.get_screen_size()
2 引用其他air脚本
from airtest.core.api import using
using("other_test.air") # 需要使用相对路径,不然找不到.air
import other_test
3 元素定位
根据对象名字定位
login_btn = poco("login_btn")
# 按钮点击
login_btn.click()
根据文本内容来定位
login_btn = poco(text="账号登录")
login_btn.click()
用正则表达式来模糊定位
login_btn = poco(textMatches="^.*账号登录.*$")
login_btn.click()
子元素
list_item = poco('list_root').child('list_item')
父节点
poco('node_item').parent()
兄弟节点
poco('user_level').sibling('user_name')
4 点击元素身上的某一点:focus、click
通过相对坐标,控制点击的具体位置。左上角(0, 0)
,右下角(1, 1)
node = poco(text='领取奖励')
# 点击节点的中心点位置, 默认点击中心位置
node.focus('center').click()
# 点击节点的靠近左上角位置
node.focus([0.1, 0.1]).click()
# 点击节点的右下角位置
node.focus([0.9, 0.9]).click()
5 等待元素出现或消失:wait_for_appearance、wait_for_disappearance
当使用wait_for_appearance
或wait_for_disappearance
时,建议处理PocoTargetTimeout
,并截图,以方便在报告中查看出错时的页面情况
try:
# 元素出现
poco(text='领取奖励').wait_for_appearance(timeout=10)
# 元素消失
# poco(text='领取奖励').wait_for_disappearance(timeout=10)
except PocoTargetTimeout:
snapshot(msg="超时: [领取奖励]元素出现")
6 拖动:drag_to
# 将star(星星)拖动到shell(贝壳)那里
poco('star').drag_to(poco('shell'))
7 滑动:swipe
# 滑动指定坐标
poco('Scroll View').swipe([0, -0.1])
# 向上滑动
poco('Scroll View').swipe('up')
# 向下滑动
poco('Scroll View').swipe('down')
封装,水平滑动和垂直滑动,把不在屏幕内部的控件滑动到屏幕内,使之可被操作
#先计算屏幕宽和高
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
android_poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
screen_width, screen_height = android_poco.get_screen_size()
'''
水平滑动,把不在屏幕内部的控件滑动到屏幕内,使之可被操作
:param element: 控件元素
:param area_left: 希望控件所在屏幕上的区域的左边缘:0~1
:param area_right: 希望控件所在屏幕上的区域的右边缘:0~1
:param swipe_pos_y: 滑动时点击的y坐标
'''
def horizontal_swipe_element_to_area(element, area_left=0.3, area_right=0.7, swipe_pos_y=320):
swipe_pos_x = (area_left+area_right)*0.5*screen_width
for i in range(50):
cur_pos = element.get_position()
cur_pos_x = cur_pos[0]
if cur_pos_x < area_left or cur_pos_x > area_right:
#如果在区域左边,则往右滑,如果在区域右边,则往左滑
swipe([swipe_pos_x ,swipe_pos_y], [2*swipe_pos_x -cur_pos_x*screen_width, swipe_pos_y])
else:
print("swipe finish")
break
'''
垂直滑动,把不在屏幕内部的控件滑动到屏幕内,使之可被操作
:param element: 控件元素
:param area_top: 希望控件所在屏幕上的区域的上边缘:0~1
:param area_bottom: 希望控件所在屏幕上的区域的下边缘:0~1
:param swipe_pos_x: 滑动时点击的x坐标
'''
def vertical_swipe_element_to_area(element, swipe_pos_x=600, area_top=0.3, area_bottom=0.7):
swipe_pos_y = (area_bottom+area_top)*0.5*screen_height
for i in range(50):
cur_pos = element.get_position()
cur_pos_y = cur_pos[1]
if cur_pos_y < area_top or cur_pos_y > area_bottom:
#如果在区域上边,则往下滑,如果在区域下边,则往上滑
swipe([swipe_pos_x,swipe_pos_y],[swipe_pos_x, 2*swipe_pos_y -cur_pos_y*screen_height])
else:
print("swipe finish")
break
8 连续滑动:swipe_along
# 获取当前手机设备
dev = device()
# 手指按照顺序依次滑过3个坐标
dev.minitouch.swipe_along([(100, 100), (200, 200), (300, 300)])
9 按住n秒拖动: hold、to
# 点击ui1保持1秒,拖动到ui2并保持1秒,然后抬起
ui1.start_gesture().hold(1).to(ui2).hold(1).up()
10 长按:long_click
# 长按,单位:秒
poco(text="长按").long_click(duration=2.0)
11 判断元素是否存在:exists
login_btn = poco(text="账号登录")
if login_btn.exists():
login_btn.click()
12 UI状态清除:invalidate
在poco
里选择出来的UI
都是代理对象,在执行同一个用例里,一个UI
控件选出来后能持续多长时间有效这个是要看Android
那回收UI
资源的策略的,每个厂商的差异比较大。
对于cocos2d-x
引擎的poco
,由于使用的是快照模式,获取到UI
状态后如果UI
状态确实发生了改变,需要调用invalidate()
进行重新获取。
btn = poco(text="领取奖励")
btn.click()
# 过了一段时间之后,再执行的时候,先调用invalidate进行重新获取代理对象
btn.invalidate()
btn.click()
13 两指挤压收缩操作:pinch
# 在给定的范围和持续时间下,在UI上两指挤压收缩操作
poco.pinch(direction='in', percent=0.6, duration=2.0, dead_zone=0.1)
14 无效的操作异常:InvalidOperationException
from poco.exceptions import *
try:
# 点击屏幕外部,此时一定为报错,捕获到InvalidOperationException
poco.click([1.1, 1.1])
except InvalidOperationException:
snapshot(msg="无效的操作")
15 节点或属性不存在异常:PocoNoSuchNodeException
from poco.exceptions import *
node = poco("not existed node")
try:
node.click()
except PocoNoSuchNodeException:
snapshot(msg="节点不存在")
try:
node.attr('text')
except PocoNoSuchNodeException:
snapshot(msg="属性不存在")
16 等待超时异常:PocoTargetTimeout
from poco.exceptions import *
try:
poco("login_btn").wait_for_appearance(timeout=10)
except PocoTargetTimeout:
snapshot(msg="login_btn 等待超时")
17 对象已被清除:PocoTargetRemovedException
from poco.exceptions import *
try:
poco('login_btn').click()
except PocoTargetRemovedException:
snapshot(msg='login_btn 对象已被清除')
五、自定义poco属性
有时候,我们可能需要自定义一些属性,方便在poco中操作,比如自定义一个selfdefineAttr
属性,
只需在PocoSDK
中的UnityNode.cs
添加想要的定义即可
public override object getAttr(string attrName)
{
switch(attrName)
{
case "selfdefineAttr":
return "Hello selfdefineAttr";
}
}
另外,如果想在Poco
渲染树中显示该属性,则在GetPayload()
函数中添加字段即可
private Dictionary<string, object> GetPayload()
{
Dictionary<string, object> payload = new Dictionary<string, object>() {
{
"selfdefineAttr", "Hello selfdefineAttr"}
};
}
最后,我们就可以在Airtest
中通过poco
的attr
方法获取到我们自定义的属性了
selfdefineAttr = poco('testNode').attr('selfdefineAttr')
print(selfdefineAttr)