使用 Airtest 實現多臺 Ios 真機的併發測試

Rita發表於2020-07-30

最近在學習Airtest,主要用於進行多臺ios真機裝置的自動化相容性測試

1、背景

Airtest是網易出品的一款基於影像識別和poco控制元件識別的一款UI自動化測試工具。Airtest的框架是網易團隊自己開發的一個影像識別框架,這個框架的祖宗就是一種新穎的圖形指令碼語言Sikuli。Sikuli這個框架的原理是這樣的,計算機使用者不需要一行行的去寫程式碼,而是用螢幕截圖的方式,用截出來的圖形擺列組合成神器的程式,這是Airtest的一部分。

另外,Airtest也基於poco這個UI控制元件搜尋框架,這個框架也是網易自家的跨平臺UI測試框架,原理類似於appium,通過控制元件的名稱,id之類的來定位目標控制元件,然後呼叫函式方法,例如click(),swipe()之類的方法來對目標控制元件進行點選或者是操作。

關於Airtest更詳細的介紹請參考Airtest官方手冊。https://airtest.doc.io.netease.com

2、環境搭建

測試環境:
MAC系統版本:macOS High Sierra 版本10.13.6
xcode10.1(新增了8-13.5.1的支援包)
ios真機若干

1)AirtestIED

指令碼的錄製使用的是AirtestIDE,AirtestIDE是一個跨平臺的UI自動化測試編輯器,下載搭建都是非常簡單的,跟隨官網的指導即可 http://airtest.netease.com
若是操控單一裝置,或執行單一指令碼,用IDE操作非常方便與靈活。若是多機操作,及多指令碼長時間操作,及使用其它的第三方庫,還是脫離AirtestIDE,會更穩定與方便。

2)脫離AirtestIDE

脫離IDE的方法主要參考了0 https://www.jianshu.com/p/9f79f9486a15方法如下:

(1)安裝並配置好python3.7
(2)安裝第三方庫:airtest與pocoui(使用pip3 install安裝)
(3)檢查cv2模組版本是否大於3.7,若不,執行以下命令:

pip3 uninstall opencv-contrib-python
pip3 install opencv-contrib-python==3.2.0.7(根據提示進行版本選擇)
以上幾步完成後,就可以脫離airtestIDE來程式設計了。

3)實現多臺ios真機的併發測試

思路:給不同的裝置分配不同的埠號,使用多程式的方式進行併發測試
關鍵程式碼片段:

#獲取接入的手機的udid列表
sub = subprocess.Popen('idevice_id -l', shell=True, close_fds=True, stdout=subprocess.PIPE)
sub.wait()
udid = sub.stdout.read().decode().splitlines()
udid = list(set(udid))
#在終端執行wda的編譯及埠轉發,各手機操作之間需要加延時
for i in range(len(udid)):
proxy = str(8100+i+1)
#使用終端編譯執行WDA,不同的手機的udid不同
os.system(xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination "id=udid[i]" test)
#給手機分配不同的埠號
os.system(iproxy proxy 8100 [udid]')
#使用程式池的方式進行併發測試
pool.apply_async(
test_air, (proxy,udid[i]……))
……
def test_air(proxy,udid……):
#連結手機
auto_setup(__file__, logdir=udid_path, devices=[
"ios:///http://127.0.0.1:%s" %(proxy)])

#真正執行測試指令碼
test_start()
#如果需要生成報告,執行
simple_report(__file__,logpath=xxx,output=xxx)

使用分配埠+多程式的方式,目前已經成功進行了多臺裝置的併發,說是併發,但考慮到WDA的編譯,各手機之間的操作還是加了一些延時,目前測試是5-7臺裝置並行測試比較穩定。

3、測試指令碼的優化

因為是相容性測試,在一臺手機上錄製完了指令碼之後,需要在多臺不同解析度的手機上進行回放,這就會降低指令碼回放時點選的成功率,因此我們需要對指令碼進行一些優化,來提升點選的成功率。
目前我想到的一些比較簡單容易實現的辦法總結如下(針對自己的業務需求,可能不適用於所有的小夥伴)

1)採用影像識別和poco混合使用的方式

比如:touch失敗時,再使用poco來嘗試點選,寫為if else的形式(官網說明:目前IOS的poco模式不如Android效果好,因此可以將影像識別的方式放在前面);

#已修改了touch介面,新增了返回值
if not touch(Template(path+r"tpl1591927997240.png", record_pos=(0.376, -0.349), resolution=(1242, 2208))):
poco("掃一掃").click()

2)點選失敗時,指令碼執行不中斷

因為我自己做的業務中,需要對app進行長時間的點選測試,中間漏掉某一項測試影響也不大,因此我使用了這個方式
針對影像識別的方式:修改swipe、touch(airtest庫/usr/local/lib/python3/site-package中的core/api.py)介面,使影像和元素都匹配不到時,不丟擲異常,繼續向下執行,目前使用的是try...except方式;
針對poco方式:修改click、swipe等(poco下的proxy.py)介面,方法同上

3)新增全域性等待時間

在進行指令碼執行時,有時成功執行了點選操作,但頁面還在載入中,就會使這一步操作跳過,從而引起下列的一系列錯誤,因此,最好在每一個操作後新增一個等待時間,即sleep。為方便,可通過新增全域性變數的形式進行新增,如下:

from airtest.core.api import * 
# airtest.core.api中包含了一個名為ST的變數,即為全域性設定

ST. OPDELAY= 3 #OPDELAY預設值為0.1,可以自定義
poco("掃一掃").click()

如果某些步驟響應時間很久,可以在語句後再單獨新增一句sleep

4)適當降低影像識別閾值

影像識別閾值threshold是用來判定一張圖片識別是否成功的閾值,例如一張圖片識別到的匹配度是0.65,而我們設定的threshold為0.7的話,Airtest會認為匹配失敗,從而進行下一次匹配。通常來說,threshold設定得越高,影像識別的精度越高,但成功率也會有所降低
如果希望修改全域性所有圖片的影像識別閾值THRESHOLD(也可以在語句中通過傳參的方式單獨改變閾值),在指令碼中新增:

from airtest.core.api import *

ST. THRESHOLD= 0.6 # THRESHOLD取值範圍為0~1之間,[0, 1],預設值是0.7

5)採用按螢幕比例進行swipe的方式

我們測試APP時,經常會有滑動操作,如果按照圖片到圖片,圖片到指定座標,座標到座標的方式進行滑動,換手機後成功率非常低,特別是螢幕大小相差較大的兩款手機,基本無法同時成功執行同一個swipe指令。
因此,我採用了按照螢幕比例進行swipe操作的方式,操作前,先實時獲取螢幕的尺寸,然後按照比例進行滑動或拖拽,程式碼如下:

def swipe_(width,height):
#獲取螢幕尺寸,用於拖拽
start_pt = (width /2, height * 0.7)
end_pt = (width /2, height * 0.3)
if not swipe(start_pt, end_pt):
poco.swipe([0.5,0.6],[0.5,0.3])

因採用先影像識別後poco的方式,所以進行了簡單封裝,後續需要進行滑動操作時,直接呼叫swipe_( )即可

6)影像識別演算法選擇

關於airtest演算法如何選擇、效能的對比可以參考:
https://www.cnblogs.com/AirtestProject/p/12170016.html
目前指令碼預設使用的是如下三種方式迴圈匹配,直至超時;

綜合考慮效能、精確度、準確度等方面,目前採用的方式是比較合理的,因此沒有進行改變,若在同一臺手機上進行指令碼錄製回放,可以將TemplateMatch方式的優先順序調製第一位,速度更快,方式如下:

from airtest.core.settings import Settings as ST     

ST.CVSTRATEGY = ["tpl", "sift","brisk"]

4、總結

時間有限寫的比較倉促簡略,用的方法也都比較簡單粗暴,後續會再進一步的優化補充,歡迎有同樣需求的小夥伴一起討論,提出寶貴的意見建議(oo)

相關文章