官網:http://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/
其他筆記:
Airtest Project 自定義啟動器批量運行腳本
解決運行Airtest腳本時opencv-contrib-python報錯的問題
Airtest Project + Jenkins 微信小程序UI自動化測試 持續集成實踐
開發環境準備
使用AirtestIDE來編寫腳本,只需要在 官網 下載最新版本AirtestIDE,解壓即可直接使用。AirtestIDE內置了Python3.6.5,airtest和poco環境,本地無需安裝python環境就能直接使用。
如果想要使用其他需要安裝的Python第三方庫,或者Python2環境,則需要進行本地python的 環境部署 ,然后在AirtestIDE設置中添加本地的Python.exe路徑,詳情參考 IDE配置。
在AirtestIDE中新建的腳本,后綴都為 .air
,但實際上在運行的時候, 運行的是.air目錄下的同名.py文件 。
腳本調試
目前AirtestIDE暫不支持斷點調試功能,因此調試腳本只能通過print
等較為簡單的方式。但是由于Airtest框架涉及到圖像識別的準確率問題,需要反復運行和調試才能確定合適的圖片與識別閾值,因此IDE特別提供了一種 選中部分代碼單獨運行
的調試功能(如下),需要注意的是,該單獨運行代碼的功能不會執行到腳本中其他代碼里的內容,可能會出現別處的變量未能初始化等情況(因此不支持poco)。
IDE還提供了一個圖片截圖預覽功能,在腳本編輯區內雙擊圖片,會彈出圖片編輯器,在圖片編輯器內點擊 Snapshot Recognition
按鈕,將會截取當前的手機屏幕畫面,并且進行一次識別,識別成功的話會在截圖上面標注出識別區域,方便進行截圖的調試。
Mac Airtest圖片編輯器疑有BUG,點擊圖片編輯器的右下角的OK或Cancel鍵后,整個IDE都會卡死,已提交Issue
目前圖片識別中,每次識別時,只要識別結果的可信度>閾值 threshold
即認為是識別通過。如果識別到錯誤的位置,可以通過調節 threshold
進行準確度調整。
Android 真機USB連接
測試機:華為
開啟開發者模式:進入設置->系統->關于手機,多次點擊版本號后進入開發者模式;
設置開發人員選項:允許USB調試、連接USB時總是彈出提示、監控ADB安裝應用、僅充電模式下允許ADB調試(可選)
關閉電腦上已經安裝的手機助手軟件, 使用USB線連接手機,手機上出現USB連接方式(傳輸照片、傳輸文件和僅充電),選擇"傳輸照片",如果在第二步中設置了僅充電模式下允許ADB調試,則需要選擇"僅充電"選項;
點擊Airtest點擊列表內對應設備的 Connect 完成連接, 若設備未刷出,點擊 refresh ADB 按鈕設備列表將會刷新;
Airtest
基于圖像識別的無侵入式測試框架。
初始化
# -*- encoding=utf8 -*-
from airtest.core.api import *
auto_setup(__file__) #自動初始化設備
錄制airtest語句
- 輔助按鍵錄制腳本
在AirtestIDE的Airtest錄制輔助窗內,包含有三種類型的錄制按鈕:
點擊某個按鈕后,在設備畫面上按下鼠標左鍵進行截圖框選,抬起鼠標左鍵完成框選。對應操作語句會自動插入編輯器腳本中。
- 腳本自動錄制
點擊自動錄制按鈕后,使用鼠標操作設備畫面,對應操作語句會自動插入到編輯器腳本中(不好用)。
airtest.core.api
Airtest的常用API大部分都列在了AirtestIDE里的Airtest輔助窗中,在使用各種常見的截圖語句時,鼠標移動到按鈕上即可看到每個接口的常用參數與返回值信息,非常方便。
Poco
基于UI控件識別的測試框架
API文檔:https://poco.readthedocs.io/en/latest/source/poco.proxy.html#poco.proxy.UIObjectProxy.exists
poco.exceptions module: https://poco.readthedocs.io/en/latest/source/poco.exceptions.html
初始化
切換Poco應用類型時,腳本編輯框會彈出提示插入初始化Poco代碼的通知窗。 確認Poco應用類型正確后,確認插入光標位置后,點擊 'Yes'
即可插入對應的Poco初始化代碼。
- Unity3D
from poco.drivers.unity3d import UnityPoco
poco = UnityPoco()
- Android native APP
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
poco = AndroidUiautomationPoco()
poco.device.wake()
poco(text='Clock').click()
切換模式
Poco Pause ->凍結模式
主要用途:查看當前畫面中各位置的UI呈現范圍。UI自動化測試中,常使用此功能進行元素定位。
進入方式:
單擊UI渲染樹上的任意條目(注意是單擊,雙擊會直接將當前UI位置插入到代碼區域);
-
點擊 Poco Pause 按鈕:
具體表現:
屏幕畫面會凍結(設備操作失效),UI渲染樹的數據也會停止刷新。
伴隨鼠標在畫面中的移動,對應位置的UI元素會被標記框標出。
鼠標左鍵點擊,可以在log輸出窗中查看對應控件的詳細屬性。
Poco Inspect ->檢視模式
主要用途:伴隨設備操作,查看不同頁面UI的渲染情況。
進入方式:
具體表現:
設備畫面可以正常交互,UI渲染樹的數據正常刷新。
伴隨鼠標在畫面中的移動,對應位置的UI元素會被標記框標出。
鼠標左鍵點擊,可以在log輸出窗中查看對應控件的詳細屬性。
Poco Recording ->錄制模式
-
單步錄制Poco腳本
如下,先通過凍結模式在UI樹中找到目標條目,雙擊直接插入poco代碼,也可以右鍵目標條目,選擇插入UI節點的Xpath代碼。
".click()"
)需要手動添加。 -
自動錄制
進入方式:
具體表現:
設備畫面可以正常交互,UI渲染樹的數據正常刷新。
伴隨鼠標在畫面中的移動,對應位置的UI元素會被標記框標出。
伴隨設備操作(點擊、滑動),即可插入對應UI節點的poco代碼。
自動錄制時,會自動插入代碼對象的操作代碼。
選擇器
在poco實例后加一對括號就可以進行UI選擇了。選擇器會遍歷所有UI,將滿足給定條件的UI都選出來并返回。括號里的參數就是所給定的條件,用屬性名值對表示,其中第一個參數固定表示 節點名 其余可選參數均表示節點的屬性及預期的屬性值。詳見: API Reference selecting UI
- Basic Selector
# select by node name
poco('bg_mission')
# select by name and other properties
poco('bg_mission', type='Button')
# 支持正則
poco(text='確定')
poco(textMatches='^據點.*$', type='Button', enable=True)
- Relative Selector
# select by direct child/offspring
items=poco('main_node').child('list_item').offspring('item')
# 可迭代對象
for item in items:
item.child('icn_item')
- Sequence Selector
tems = poco('main_node').child('list_item').offspring('item')
print(items[0].child('material_name').get_text())
print(items[1].child('material_name').get_text())
讀取屬性
mission_btn = poco('bg_mission')
print(mission_btn.attr('type')) # 'Button'
print(mission_btn.get_text()) # '查詢停車費'
print(mission_btn.attr('text')) # '查詢停車費' equivalent to .get_text()
print(mission_btn.exists()) # True/False, exists in the screen or not
操作對象
- 點擊 click
點擊默認點在 anchorPoint 上,每個UI都會有一個 anchorPoint ,也就是檢視器(Inspector)中UI包圍盒的那個紅點,大部分情況下 anchorPoint 都在UI包圍盒的正中央。如果想指定其他的點擊位置,可以傳一個參數到click
方法中,這個參數是一個用list或tuple表示的2維向量,其 [x, y] 值分別表示相對于包圍盒左上角的偏移量,左上角為[0, 0]
,右下角為[1, 1]
poco('bg_mission').click()
poco('bg_mission').click('center')
poco('bg_mission').click([0.5, 0.5]) # equivalent to center
poco('bg_mission').focus([0.5, 0.5]).click() # equivalent to above expression
- 局部定位 focus
所有UI相關的操作都默認以UI的 anchorPoint 為操作點,如果想自定義一個點那么可以使用focus
方法。調用此方法將返回 新的 設置了默認 焦點 的UI,重復調用則以最后一次所調用的為準。focus
所使用的是局部坐標系,因此同樣是UI包圍盒的左上角為原點,x軸向右,y軸向下,并且包圍盒長寬均為單位1。很顯然中心點就是[0.5, 0.5]
.
poco('bg_mission').focus('center').click() # click the center
- 滑動 swipe
swipe操作同樣是以 anchorPoint 為起點,如果你想改變起點請使用focus
方法,然后朝給定向量所代表的方向滑動,距離也就是向量的長度.
joystick = 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)
- 拖拽 drag
與swipe
不同的是,darg
是從一個UI拖到另一個UI,而swipe
是將一個UI朝某個方向拖動。
poco(text='AAA').drag_to(poco(text='BBB'))```
將 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。 如果超時了還沒有出現,同樣也會返回,但是調用這個UI的操作時會報錯。(我真被這里坑死了)
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
- 截圖 snapshot
截屏幕并以base64編碼返回。截圖的格式(png, jpg, …)由對應的sdk實現決定,大多數情況下是png。
from base64 import b64decode
b64img, fmt = poco.snapshot(width=720)
open('screen.{}'.format(fmt), 'wb').write(b64decode(b64img))
- 全局操作
在沒有選定或指定UI的情況下也可以進行操作(模擬輸入),也叫全局操作。
poco.click([0.5, 0.5]) # click the center of screen
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]
poco.swipe(point_a, center)
# swipe from A by given direction
direction = [0.1, 0]
poco.swipe(point_a, direction=direction)
常見異常
from poco.exceptions import PocoTargetTimeout
from poco.exceptions import PocoNoSuchNodeException
使用命令行運行腳本
官方文檔: http://airtest.netease.com/docs/docs_AirtestIDE-zh_CN/4_run_script/run_script.html#id3
運行腳本:
PS E:\treasure\Airtest> [python -m] airtest run parkIndex.air --log E:/CatJmx/ParkTest/Airtest/log
可選參數:
- [--recording]
讓airtest自動對腳本執行過程中的手機屏幕進行錄制操作。錄制完成后,將自動生成一個命令格式類似于 recording_0.mp4 的文件到腳本生成的log目錄中。在最后生成報告時,這個mp4文件會默認顯示在HTML報告頁面里。
- [--device]
設備字符串,什么都不填寫,會默認取當前連接中的第一臺手機;字串完整定義格式:Android://<adbhost[localhost]>:<adbport[5037]>/<serialno>
- [--log]
指定生成的log目錄路徑,默認為腳本所在目錄
生成報告
腳本運行過程,與報告生成過程是獨立的兩個步驟,因此在運行過 airtest run script.air后,假如沒有指定 --log log/ 參數,Airtest 將把生成的log內容放到當前命令行的執行目錄里(如果指定了 --log 參數,log內容與截圖將會放在指定目錄里)
PS E:\treasure\Airtest> [python -m] airtest report parkIndex.air --log_root E:/CatJmx/ParkTest/Airtest/log --outfile log/log.html --lang zh --export E:/CatJmx/ParkTest/Airtest --plugin poco.utils.airtest.report
可選參數:
[--log_root]
日志根目錄,日志文件應該是 log_root / log.txt[--outfile]
指定輸出html文件路徑,默認為log.html[--static_root]
靜態文件根目錄[--lang zh]
設置報告語言[--export]
在使用 airtest report 指令生成的報告中,使用了絕對路徑來訪問里面的圖片文件,同時HTML報告中訪問的靜態css與js資源文件,也是硬盤上的絕對路徑(默認在airtest的安裝目錄下的report文件夾里)。因此,假如想要發送報告給其他人觀看,就必須要在命令行末尾加上 --export 導出目錄,將報告導出到一個指定目錄中,然后將整個目錄發送給別人查看;[-plugin poco.utils.airtest.report]
默認報告是airtest的專屬報告,對于poco語句的支持不夠完善,需要使用插件的形式來補充;-
[--plugin airtest_selenium.report]
如果腳本中使用了selenium插件,在生成報告的命令行最后,需要加入 --plugin airtest_selenium.report,可以讓報告支持selenium元素;
[To be continued...]