Airtest 常用方法


幾個添加設備參數的例子

# 方法一:在auto_setup()接口添加設備
auto_setup(__file__,devices=["Android://127.0.0.1:5037/SJE5T17B17"])

# 方法二:用connect_device()方法連接設備
dev = connect_device("Android://127.0.0.1:5037/SJE5T17B17")

# 方法三:用init_device()方法連接設備
init_device(platform="Android",uuid="SJE5T17B17")

 

過濾非嚴重級別的log

__author__ = "Airtest"

import logging
logger = logging.getLogger("airtest")
logger.setLevel(logging.ERROR)

 

循環點擊某個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:定義dongjiepoco
freeze_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 = print
def 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 appears
poco('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 screen
poco.long_click([0.5, 0.5], duration=3)

滑動(swipe)

# swipe from A to B
point_a = [0.1, 0.1]
center = [0.5, 0.5]
poco.swipe(point_a, center)

# swipe from A by given direction
direction = [0.1, 0]
poco.swipe(point_a, direction=direction)

截屏幕並以base64編碼返回。截圖的格式(png, jpg, …)由對應的sdk實現決定,大多數情況下是png。詳見 ScreenInterface.getScreen

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 PocoTargetTimeout

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

PocoNoSuchNodeException(沒有這個控件)

from poco.exceptions import PocoNoSuchNodeException

img = 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-8

from poco.drivers.unity3d import UnityPoco

poco = 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-8

from poco.drivers.unity3d import UnityPoco

poco = 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 actually

invisible_obj = poco('result_panel', type='Layer')
print(invisible_obj.exists())  # => False. This UI is not visible to user.

Swipe滑動

# coding=utf-8

from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

# swipe the list view up
poco('Scroll View').swipe([0, -0.1])
poco('Scroll View').swipe('up')  # the same as above, also have down/left/right
poco('Scroll View').swipe('down')

# perform swipe without UI selected
x, 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 B
poco.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-8

from poco.drivers.unity3d import UnityPoco

poco = 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-8

import time
from poco.drivers.unity3d import UnityPoco

poco = 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-8

from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

balloonfish_image = poco(text='balloonfish').focus([0.5, -3])
balloonfish_image.long_click()

下面的示例表明,focus是一個不會影響原始UI的不可變方法

# coding=utf-8

from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

# focus is immutable
fish = poco('fish').child(type='Image')
fish_right_edge = fish.focus([1, 0.5])
fish.long_click()  # still click the center
time.sleep(0.2)
fish_right_edge.long_click()  # will click the right edge
time.sleep(0.2)

下面的示例演示如何使用拖動來滾動列表。

# coding=utf-8

import time
from poco.drivers.unity3d import UnityPoco

poco = 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-8

import time
from poco.drivers.unity3d import UnityPoco

poco = 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-8

import time
from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

for name in poco('plays').offspring('fish').child('name'):
    print(name.get_text())  # pearl/shark/balloonfish

下面例子展示了怎么樣在商城界面中購買當前屏幕的所有商品。

# coding=utf-8

poco = 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-8

from poco.drivers.unity3d import UnityPoco
from poco.exceptions import InvalidOperationException

poco = UnityPoco()

try:
    poco.click([1.1, 1.1])  # click outside screen
except InvalidOperationException:
    print('oops')

如果從一個不存在的UI控件讀取屬性或控制它,那就會出現這個異常。測試一個UI控件是否存在可以調用UI代理的 .exists() 方法。

# coding=utf-8

from poco.drivers.unity3d import UnityPoco
from poco.exceptions import PocoNoSuchNodeException

poco = 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-8

from poco.drivers.unity3d import UnityPoco
from airtest.core.api import connect_device
from poco.exceptions import PocoTargetTimeout


poco = UnityPoco()

# UI is very slow
poco('btn_start').click()
star = poco('star')
try:
    star.wait_for_appearance(timeout=3)  # wait until appearance within 3s
except PocoTargetTimeout:
    print('oops!')
    time.sleep(1)

與上面 PocoTargetTimeout 不同,如果操作速度遠遠慢於UI變化的速度,那*很可能*會出現這個異常。這個異常僅當去訪問或操作一個剛才還在但現在不在的UI控件才會出現,並且一般情況下基本不會出現。 

下面例子展示點擊一個已經不存在的UI控件的效果

# coding=utf-8

from poco.exceptions import PocoTargetRemovedException, PocoNoSuchNodeException
# 這個異常僅會在一些poco-sdk實現中,所以更可靠的做法是必要的情況下顯示地去調用 .exists() 去判斷UI是否存在。

poco
= Poco(...) start = poco('start') print(start.exists()) # => True. start.click() print(start.exists()) # => False try: 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-8

from poco.drivers.unity3d import UnityPoco
from airtest.core.api import connect_device


poco = UnityPoco()

# no PocoTargetRemovedException case
start = poco('start')
print(start.exists())  # => True.
start.click()
print(start.exists())  # => False

# 重要提示:在Unity3d軟件中,此操作將點擊與之前相同的坐標
# 不管發生什么
start.click()

一些復雜的測試用例中,不可能只是不斷地主動控制或者讀取屬性。通過被動地獲取UI狀態改變的事件,這樣有助於寫出不混亂的測試腳本。Poco提供了簡單的輪詢機制去同時輪詢1個或多個UI控件,所謂輪詢就是依次判斷UI是否存在。

下面例子展示的是最簡單的UI同步

# coding=utf-8

from poco.drivers.unity3d import UnityPoco

poco = UnityPoco()

# start and waiting for switching scene
start_btn = poco('start')
start_btn.click()
start_btn.wait_for_disappearance()

# waiting for the scene ready then click
exit_btn = poco('exit')
exit_btn.wait_for_appearance()
exit_btn.click()

下面例子展示輪詢UI時等待 任意一個 UI出現就往下走

# coding=utf-8

from poco.drivers.unity3d import UnityPoco
from poco.exceptions import PocoTargetTimeout

poco = UnityPoco()

bomb_count = 0
while 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-8

import time
from poco.drivers.unity3d import UnityPoco

poco = 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 time
from poco.drivers.unity3d import UnityPoco

poco = 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 time
from poco.drivers.unity3d import UnityPoco

poco = 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM