Airtest自动化测试 常用方法
发布日期:2021-05-07 23:49:26 浏览次数:13 分类:精选文章

本文共 13514 字,大约阅读时间需要 45 分钟。

Airtest自动化测试 常用方法

循环点击某个list元素合集

for item in range(len(freeze_poco("List").child())):     item = str(item)     poco("NestStarReward").offspring(item).offspring("T").click()

冻结poco,使操作更快

freeze_poco = poco.freeze()  # TODO:定义dongjiepocofreeze_poco("TheExpDlg(Clone)").offspring(item).click()

判断元素不存在则点击

if not poco("TeamDlg(Clone)").offspring("Categories").offspring("item1").offspring("Text").exists():    poco("CurrentCategory").click()

判断元素存在则点击

if poco("TeamDlg(Clone)").offspring("Categories").offspring("item1").offspring("Text").exists():    poco("CurrentCategory").click()

在脚本中重置下条case的环境

try:    finally:

判断多个控件同时存在

with poco.freeze() as freeze_poco:    if freeze_poco("DetailsBtn").exists() and \            freeze_poco("CurrentExp").exists() and \            freeze_poco("Chests").child("item0").exists() and \            freeze_poco("Chests").child("item1").exists() and \            freeze_poco("Chests").child("item2").exists() and \            freeze_poco("Chests").child("item3").exists() and \            freeze_poco("Chests").child("item4").exists() and \            freeze_poco("LeftView").child("Exp").exists() and \            freeze_poco("LeftView").child("Exp500").exists() and \            freeze_poco("LeftView").child("Exp1000").exists() and \            freeze_poco("LeftView").child("Exp500").child("box").exists() and \            freeze_poco("LeftView").child("Exp1000").child("box").exists() and \            freeze_poco("DailyActivityDlg(Clone)").offspring("RightView").offspring("Go").exists()

保证该控件在可点击范围方法

def butpos(butpos,pos1=0.4,pos2=0.81,high=1330,low=930,lows=482):    """把不在屏幕内部的控件滑动到屏幕内,使之可被操作:param butpos: 控件元素:param pos1: 希望控件所在屏幕上的最低限:param pos2: 希望控件所在屏幕上的最上限:param high: 固定坐标:param low: 滑动起始或终点位置:param lows: 滑动起始或终点位置:return:    """    for i in range(50):        but = butpos.get_position()        if but[1] < pos1:            swipe([high, lows], [high, low], 5)        elif but[1] > pos2:            swipe([high, low], [high, lows], 5)        else:            break

获取手机屏幕大小

def _get_screen_size(devices):    '获取手机屏幕大小'    size_str = os.popen(f'adb -s {devices} shell wm size').read()    if not size_str:        print('请安装 ADB 及驱动并配置环境变量')        sys.exit()    m = re.search(r'(\d+)x(\d+)', size_str)    if m:        sizeheight = "{height}".format(height=m.group(1))        sizewidth = "{width}".format(width=m.group(2))        return int(sizeheight),int(sizewidth)    return "1920x1080"

把print输出成log

_print = printdef print(*args, **kwargs):    _print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), *args, **kwargs)

使用函数定义pcoo

def deviceconnect(devices):    """    用于poco实例化的公用方法    :param devices: 制定设备    :return:    """    dev = connect_device("android:///" + devices)    poco = UnityPoco(device=dev)    return poco

所有UI相关的操作都默认以UI的 anchorPoint 为操作点,如果想自定义一个点那么可以使用 focus 方法。调用此方法将返回 新的 设置了默认 焦点 的UI,重复调用则以最后一次所调用的为准。focus 所使用的是局部坐标系,因此同样是UI包围盒的左上角为原点,x轴向右,y轴向下,并且包围盒长宽均为单位1。很显然中心点就是 [0.5, 0.5] 。下面的例子会展示一些常用的用法。

poco('bg_mission').focus('center').click()  # click the center

将 focus 和 drag_to 结合使用还能产生卷动(scroll)的效果,下面例子展示了如何将一个列表向上卷动半页。

scrollView = poco(type='ScollView')scrollView.focus([0.5, 0.8]).drag_to(scrollView.focus([0.5, 0.2]))

在给定时间内等待一个UI出现并返回这个UI,如果已经存在画面中了那就直接返回这个UI。 如果超时了还没有出现,同样也会返回,但是调用这个UI的操作时会报错。类似的操作还有wait_for_appearance

poco('bg_mission').wait(5).click()  # wait 5 seconds at most,click once the object appearspoco('bg_mission').wait(5).exists()  # wait 5 seconds at most,return Exists or Not Exists

点击(click)

poco.click([0.5, 0.5])  # click the center of screenpoco.long_click([0.5, 0.5], duration=3)

滑动(swipe)

# swipe from A to Bpoint_a = [0.1, 0.1]center = [0.5, 0.5]poco.swipe(point_a, center)# swipe from A by given directiondirection = [0.1, 0]poco.swipe(point_a, direction=direction)

截屏幕并以base64编码返回。截图的格式(png, jpg, …)由对应的sdk实现决定,大多数情况下是png。详见

from base64 import b64decode# 注意:在poco的某些引擎实现中不支持快照。b64img, fmt = poco.snapshot(width=720)open('screen.{}'.format(fmt), 'wb').write(b64decode(b64img))

异常处理(Exceptions)

PocoTargetTimeout(控件出现超时)

from poco.exceptions import PocoTargetTimeouttry:    poco('guide_panel', type='ImageView').wait_for_appearance()except PocoTargetTimeout:    # bugs here as the panel not shown    raise

PocoNoSuchNodeException(没有这个控件)

from poco.exceptions import PocoNoSuchNodeExceptionimg = poco('guide_panel', type='ImageView')try:    if not img.exists():        img.click()except PocoNoSuchNodeException:    # If attempt to operate inexistent nodes, an exception will be thrown    pass

最简单的操作就是点击(click),也可以长按(long click),按多久都行,下面例子展示点击和长按各自的效果。

# coding=utf-8from poco.drivers.unity3d import UnityPocopoco = UnityPoco()poco('btn_start').click()poco('basic').click()poco('star_single').long_click()poco('star_single').long_click(duration=5)

如果从一个不存在的UI里读取属性值或者操作它,会触发异常,如果你不确定那个UI是否存在,可以调用 .exists() 来判断。在具体的测试用例中,如果因为UI不存在出现了异常,这很有可能是game/app的bug,不要强行忽略

# coding=utf-8from poco.drivers.unity3d import UnityPocopoco = UnityPoco()btn = poco('btn_start')btn.click()print(btn.get_text())  # => 'Start'intro = poco('introduction')print(intro.get_text())  # => 'xxxx'print(intro.attr('text'))  # => 'xxxx'print(intro.attr('type'))  # => 'Text'print(intro.attr('texture'))  # => None. Because there is no texture on Text.print(intro.attr('foo-bar'))  # => None. Because "intro" dose not have an attribute named "foo-bar".intro.click()  # Perform a click on any UI objects are allowed.obj = poco('foo-bar', type='FooBar')print(obj.exists())  # => False. This UI does not exist actuallyinvisible_obj = poco('result_panel', type='Layer')print(invisible_obj.exists())  # => False. This UI is not visible to user.

Swipe滑动

# coding=utf-8from poco.drivers.unity3d import UnityPocopoco = UnityPoco()# swipe the list view uppoco('Scroll View').swipe([0, -0.1])poco('Scroll View').swipe('up')  # the same as above, also have down/left/rightpoco('Scroll View').swipe('down')# perform swipe without UI selectedx, y = poco('Scroll View').get_position()end = [x, y - 0.1]dir = [0, -0.1]poco.swipe([x, y], end)  # drag from point A to point Bpoco.swipe([x, y], direction=dir)  # drag from point A toward given direction and length

在UI自动化测试中,最关键的就是将目标UI选择出来。一般情况下,通过名字选择是最简单的方式,但是在一些情况下,并不是每个UI控件都有命名,特别是通过代码动态生成和加载的UI,一般都没有一个有意义的名字。Poco提供了强大有效的各种选择方式,不仅可以通过UI单一的属性选择,还可以通过UI之间的层次关系和位置关系来选择出目标UI。更强大的是,以上三种模式可以进行任意串联或者组合使用,这样基本上能应付所有情况了。

下面的例子将展示如何在复杂层次里选出对应的UI控件

# coding=utf-8from poco.drivers.unity3d import UnityPocopoco = UnityPoco()items = poco('main_node').child('list_item').offspring('name'):first_one = items[0]print(first_one.get_text())  # => '1/2活力药剂'first_one.click()

poco里的坐标的取值范围是相对于屏幕的,屏幕的宽和高都为单位1,因此也叫百分比坐标。当你需要和某个UI控件附近的UI控件交互或者要点击某个按钮的边缘而不是中间时,那可以用 局部定位 。

总的来说,和UI控件交互最终都是和坐标交互,例如点击一个按钮实际上就是点击某个坐标。 局部定位 就可以基于某个UI的左上角进行偏移,然后可以实现点击到这个UI控件内的各个坐标甚至UI外面的其他坐标。

# coding=utf-8import timefrom poco.drivers.unity3d import UnityPocopoco = UnityPoco()image = poco('fish').child(type='Image')image.focus('center').long_click()time.sleep(0.2)image.focus([0.1, 0.1]).long_click()time.sleep(0.2)image.focus([0.9, 0.9]).long_click()time.sleep(0.2)image.focus([0.5, 0.9]).long_click()time.sleep(0.2)

也可以在选中的UI外单击。通过它的名字标签点击一些模型是非常有用的

# coding=utf-8from poco.drivers.unity3d import UnityPocopoco = UnityPoco()balloonfish_image = poco(text='balloonfish').focus([0.5, -3])balloonfish_image.long_click()

下面的示例表明,focus是一个不会影响原始UI的不可变方法

# coding=utf-8from poco.drivers.unity3d import UnityPocopoco = UnityPoco()# focus is immutablefish = poco('fish').child(type='Image')fish_right_edge = fish.focus([1, 0.5])fish.long_click()  # still click the centertime.sleep(0.2)fish_right_edge.long_click()  # will click the right edgetime.sleep(0.2)

下面的示例演示如何使用拖动来滚动列表。

# coding=utf-8import timefrom poco.drivers.unity3d import UnityPocopoco = UnityPoco()listView = poco('Scroll View')listView.focus([0.5, 0.8]).drag_to(listView.focus([0.5, 0.2]))time.sleep(1)

Poco提供了非常简单的方式来处理一系列UI交互,直接用for循环进行迭代遍历即可。在for循环中,每次迭代的对象都是一个UI代理,所以可以像之前说的那样,去访问对象的属性和进行对象操作。

下面的示例展示了如何遍历拖动

# coding=utf-8import timefrom poco.drivers.unity3d import UnityPocopoco = UnityPoco()poco('btn_start').click()poco(text='drag drop').click()time.sleep(1.5)shell = poco('shell').focus('center')for star in poco('star'):    star.drag_to(shell)time.sleep(1)assert poco('scoreVal').get_text() == "100", "score correct."  # 这是另一种断言方法poco('btn_back', type='Button').click()

下面是另一个遍历模型所有名称的示例

# coding=utf-8import timefrom poco.drivers.unity3d import UnityPocopoco = UnityPoco()for name in poco('plays').offspring('fish').child('name'):    print(name.get_text())  # pearl/shark/balloonfish

下面例子展示了怎么样在商城界面中购买当前屏幕的所有商品。

# coding=utf-8poco = Poco(...)bought_items = set()for item in poco('main_node').child('list_item').offspring('name'):    # get its text value    item_name = item.get_text()    # markdown the bought item    if item_name not in bought_items:        item.click()        bought_items.add(item_name)

这个异常特指无效的操作,或者说不起作用的操作,例如点击到了屏幕外面,或者对一个图片设置text属性(输入框才能设置text属性)。虽然这个异常并不会对应用造成什么实质性影响,但是还是要尽可能避免,以免测试脚本里逻辑混乱或测试结果不稳定。

# coding=utf-8from poco.drivers.unity3d import UnityPocofrom poco.exceptions import InvalidOperationExceptionpoco = UnityPoco()try:    poco.click([1.1, 1.1])  # click outside screenexcept InvalidOperationException:    print('oops')

如果从一个不存在的UI控件读取属性或控制它,那就会出现这个异常。测试一个UI控件是否存在可以调用UI代理的 .exists() 方法。

# coding=utf-8from poco.drivers.unity3d import UnityPocofrom poco.exceptions import PocoNoSuchNodeExceptionpoco = UnityPoco()node = poco('not existed node')  # select永远不会引发任何异常try:    node.click()except PocoNoSuchNodeException:    print('oops!')try:    node.attr('text')except PocoNoSuchNodeException:    print('oops!')print(node.exists())  # => 假的。此方法不会引发

这个异常只会在你主动等待UI出现或消失时抛出,和 PocoNoSuchNodeException 不一样,当你的操作速度太快,界面来不及跟着变化的话,你只会遇到 PocoNoSuchNodeException 而不是 PocoTargetTimeout ,其实就是在那个UI还没有出现的时候就想要进行操作。

下面的例子展示测试脚本如何与UI保持同步,并处理 PocoTargetTimeout 异常

# coding=utf-8from poco.drivers.unity3d import UnityPocofrom airtest.core.api import connect_devicefrom poco.exceptions import PocoTargetTimeoutpoco = UnityPoco()# UI is very slowpoco('btn_start').click()star = poco('star')try:    star.wait_for_appearance(timeout=3)  # wait until appearance within 3sexcept PocoTargetTimeout:    print('oops!')    time.sleep(1)

与上面 PocoTargetTimeout 不同,如果操作速度远远慢于UI变化的速度,那很可能会出现这个异常。这个异常仅当去访问或操作一个刚才还在但现在不在的UI控件才会出现,并且一般情况下基本不会出现。

下面例子展示点击一个已经不存在的UI控件的效果

# coding=utf-8from poco.exceptions import PocoTargetRemovedException, PocoNoSuchNodeException# 这个异常仅会在一些poco-sdk实现中,所以更可靠的做法是必要的情况下显示地去调用 .exists() 去判断UI是否存在。poco = Poco(...)start = poco('start')print(start.exists())  # => True.start.click()print(start.exists())  # => Falsetry:    start.click()except PocoTargetRemovedException:    print('oops!')# IMPORTANT NOTE:# `start2` is different from `start` !# `start` is tracking the UI at initial and it knows itself was removed but `start2`# does not know anything before.start2 = poco('start')try:    start2.click()except PocoNoSuchNodeException:    print('oops!')

这个异常仅会在一些poco-sdk实现中,所以更可靠的做法是必要的情况下显示地去调用 .exists() 去判断UI是否存在。

在poco.drivers.std。StdPoco,这个异常从未被提出!

# coding=utf-8from poco.drivers.unity3d import UnityPocofrom airtest.core.api import connect_devicepoco = UnityPoco()# no PocoTargetRemovedException casestart = poco('start')print(start.exists())  # => True.start.click()print(start.exists())  # => False# 重要提示:在Unity3d软件中,此操作将点击与之前相同的坐标# 不管发生什么start.click()

一些复杂的测试用例中,不可能只是不断地主动控制或者读取属性。通过被动地获取UI状态改变的事件,这样有助于写出不混乱的测试脚本。Poco提供了简单的轮询机制去同时轮询1个或多个UI控件,所谓轮询就是依次判断UI是否存在。

下面例子展示的是最简单的UI同步

# coding=utf-8from poco.drivers.unity3d import UnityPocopoco = UnityPoco()# start and waiting for switching scenestart_btn = poco('start')start_btn.click()start_btn.wait_for_disappearance()# waiting for the scene ready then clickexit_btn = poco('exit')exit_btn.wait_for_appearance()exit_btn.click()

下面例子展示轮询UI时等待 任意一个 UI出现就往下走

# coding=utf-8from poco.drivers.unity3d import UnityPocofrom poco.exceptions import PocoTargetTimeoutpoco = UnityPoco()bomb_count = 0while True:    blue_fish = poco('fish_emitter').child('blue')    yellow_fish = poco('fish_emitter').child('yellow')    bomb = poco('fish_emitter').child('bomb')    fish = poco.wait_for_any([blue_fish, yellow_fish, bomb])    if fish is bomb:        # 跳过炸弹,数到3退出        bomb_count += 1        if bomb_count > 3:            return    else:        # 否则点击鱼收集。        fish.click()    time.sleep(2.5)

下面例子展示轮询UI时等待 所有 UI出现才往下走

# coding=utf-8import timefrom poco.drivers.unity3d import UnityPocopoco = UnityPoco()poco(text='wait UI 2').click()blue_fish = poco('fish_area').child('blue')yellow_fish = poco('fish_area').child('yellow')shark = poco('fish_area').child('black')poco.wait_for_all([blue_fish, yellow_fish, shark])poco('btn_back').click()time.sleep(2.5)

介绍一种加快UI操作速度的一种方法(即冻结UI),只是对于复杂的选择和UI遍历有效,如果只是简单的按名字选择请不要用这种方法,因为一点效果都没有冻结UI其实就是将当前界面的层次结构包括所有UI的属性信息抓取并存到内存里,在跟UI交互时就直接从内存里读取UI属性,而不用在发送rpc请求到game/app里去操作UI。好处就是一次抓取(消耗几百毫秒),可以使用多次,读取UI属性几乎不消耗时间,同时坏处就是,你需要手动处理UI同步,如果抓取了层次结构后,某个UI控件位置发生了变化,此时如果仍然点击这个UI的话,就会点击到原来的位置上,而不是最新的位置,这很容易导致奇怪的测试结果

下面两个例子分别展示使用了冻结UI和不使用冻结UI的效果区别

Freezing UI

import timefrom poco.drivers.unity3d import UnityPocopoco = UnityPoco()with poco.freeze() as frozen_poco:    t0 = time.time()    for item in frozen_poco('Scroll View').offspring(type='Text'):        print item.get_text()    t1 = time.time()    print t1 - t0  # 大约6 ~ 8秒

No Freezing UI

import timefrom poco.drivers.unity3d import UnityPocopoco = UnityPoco()t0 = time.time()for item in poco('Scroll View').offspring(type='Text'):    print item.get_text()t1 = time.time()print t1 - t0  # 约50 ~ 60 s
上一篇:PostgreSQL 数据库函数 字符串操作
下一篇:Airtest自动化测试 Docs airtest.core.android package

发表评论

最新留言

关注你微信了!
[***.104.42.241]2025年03月29日 05时27分56秒