Airtest圖像識別測試工具原理解讀&最佳實踐

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/06/19/17490390.html
-Advertisement-
Play Games

Airtest是一個跨平臺的、基於圖像識別的UI自動化測試框架,適用於游戲和App,支持平臺有Windows、Android和iOS。Airtest框架基於一種圖形腳本語言Sikuli,引用該框架後,不再需要一行行的寫代碼,通過截取按鈕或輸入框的圖片,用圖片組成測試場景,這種方式學習成本低,簡單易上... ...


1 Airtest簡介

Airtest是一個跨平臺的、基於圖像識別的UI自動化測試框架,適用於游戲和App,支持平臺有Windows、Android和iOS。Airtest框架基於一種圖形腳本語言Sikuli,引用該框架後,不再需要一行行的寫代碼,通過截取按鈕或輸入框的圖片,用圖片組成測試場景,這種方式學習成本低,簡單易上手。

2 Airtest實踐

APP接入流水線過程中,賽博平臺只支持air腳本,因此需要對京管家APP的UI自動化腳本進行的改造。如截圖可見,AirtestIDE的主界面由菜單欄、快捷工具欄和多個視窗組成,初始佈局中的“設備視窗”是工具的設備連接交互區域。
air腳本生成步驟:

  1. 通過adb連接手機或模擬器
  2. 安裝應用APK
  3. 運行應用並截圖
  4. 模擬用戶輸入(點擊、滑動、按鍵)
  5. 卸載應用

通過以上步驟自動生成了 .air腳本,調試過程中我們可以在IDE中運行代碼,支持多行運行以及單行運行,調試通過後可在本地或伺服器以命令行的方式運行腳本:
.air腳本運行方式:airtest run “path to your .air dir” —device Android
.air腳本生成報告的方式:airtest report “path to your .air dir”

3 Airtest定位方式解析

IDE的log查看視窗會時時列印腳本執行的日誌,從中可以看出通過圖片解析執行位置的過程。下麵就以touch方法為例,解析Airtest如何通過圖片獲取到元素位置從而觸發點擊操作。

@logwrap
def touch(v, times=1, **kwargs):
    """
    Perform the touch action on the device screen
    :param v: target to touch, either a ``Template`` instance or absolute coordinates (x, y)
    :param times: how many touches to be performed
    :param kwargs: platform specific `kwargs`, please refer to corresponding docs
    :return: finial position to be clicked, e.g. (100, 100)
    :platforms: Android, Windows, iOS
    """
    if isinstance(v, Template):
        pos = loop_find(v, timeout=ST.FIND_TIMEOUT)
    else:
        try_log_screen()
        pos = v
    for _ in range(times):
        G.DEVICE.touch(pos, **kwargs)
        time.sleep(0.05)
    delay_after_operation()
    return pos

click = touch  # click is alias of t

該方法通過loop_find獲取坐標,然後執行點擊操作 G.DEVICE.touch(pos, kwargs),接下來看loop_find如何根據模板轉換為坐標。

@logwrap
def loop_find(query, timeout=ST.FIND_TIMEOUT, threshold=None, interval=0.5, intervalfunc=None):
    """
    Search for image template in the screen until timeout
    Args:
        query: image template to be found in screenshot
        timeout: time interval how long to look for the image template
        threshold: default is None
        interval: sleep interval before next attempt to find the image template
        intervalfunc: function that is executed after unsuccessful attempt to find the image template
    Raises:
        TargetNotFoundError: when image template is not found in screenshot
    Returns:
        TargetNotFoundError if image template not found, otherwise returns the position where the image template has
        been found in screenshot
    """
    G.LOGGING.info("Try finding: %s", query)
    start_time = time.time()
    while True:
        screen = G.DEVICE.snapshot(filename=None, quality=ST.SNAPSHOT_QUALITY)
        if screen is None:
            G.LOGGING.warning("Screen is None, may be locked")
        else:
            if threshold:
                query.threshold = threshold
            match_pos = query.match_in(screen)
            if match_pos:
                try_log_screen(screen)
                return match_pos
        if intervalfunc is not None:
            intervalfunc()
        # 超時則raise,未超時則進行下次迴圈:
        if (time.time() - start_time) > timeout:
            try_log_screen(screen)
            raise TargetNotFoundError('Picture %s not found in screen' % query)
        else:
            t

首先截取手機屏幕match_pos = query.match_in(screen),然後對比傳參圖片與截屏來獲取圖片所在位置match_pos = query.match_in(screen)。接下來看match_in方法的邏輯:

def match_in(self, screen):
    match_result = self._cv_match(screen)
    G.LOGGING.debug("match result: %s", match_result)
    if not match_result:
        return None
    focus_pos = TargetPos().getXY(match_result, self.target_pos)
    return focus_pos

裡面有個關鍵方法:match_result = self._cv_match(screen)

@logwrap
def _cv_match(self, screen):
    # in case image file not exist in current directory:
    ori_image = self._imread()
    image = self._resize_image(ori_image, screen, ST.RESIZE_METHOD)
    ret = None
    for method in ST.CVSTRATEGY:
        # get function definition and execute:
        func = MATCHING_METHODS.get(method, None)
        if func is None:
            raise InvalidMatchingMethodError("Undefined method in CVSTRATEGY: '%s', try 'kaze'/'brisk'/'akaze'/'orb'/'surf'/'sift'/'brief' instead." % method)
        else:
            if method in ["mstpl", "gmstpl"]:
                ret = self._try_match(func, ori_image, screen, threshold=self.threshold, rgb=self.rgb, record_pos=self.record_pos,
                                        resolution=self.resolution, scale_max=self.scale_max, scale_step=self.scale_step)
            else:
                ret = self._try_match(func, image, screen, threshold=self.threshold, rgb=self.rgb)
        if ret:
            break
    return ret

首先讀取圖片調整圖片尺寸,從而提升匹配成功率:
image = self._resize_image(ori_image, screen, ST.RESIZE_METHOD)
接下來是迴圈遍歷匹配方法for method in ST.CVSTRATEGY。而ST.CVSTRATEGY的枚舉值:

CVSTRATEGY = ["mstpl", "tpl", "surf", "brisk"]
if LooseVersion(cv2.__version__) > LooseVersion('3.4.2'):
    CVSTRATEGY = ["mstpl", "tpl", "sift", "brisk"]

func = MATCHING_METHODS.get(method, None),func可能的取值有mstpl、tpl、surf、shift、brisk,無論哪種模式都調到了共同的方法_try_math

if method in ["mstpl", "gmstpl"]:
    ret = self._try_match(func, ori_image, screen, threshold=self.threshold, rgb=self.rgb, record_pos=self.record_pos,
                            resolution=self.resolution, scale_max=self.scale_max, scale_step=self.scale_step)
else:
    ret = self._try_match(func, image, screen, threshold=self.threshold, rgb=self.rgb)

而_try_math方法中都是調用的func的方法find_best_result()

@staticmethod
def _try_match(func, *args, **kwargs):
    G.LOGGING.debug("try match with %s" % func.__name__)
    try:
        ret = func(*args, **kwargs).find_best_result()
    except aircv.NoModuleError as err:
        G.LOGGING.warning("'surf'/'sift'/'brief' is in opencv-contrib module. You can use 'tpl'/'kaze'/'brisk'/'akaze'/'orb' in CVSTRATEGY, or reinstall opencv with the contrib module.")
        return None
    except aircv.BaseError as err:
        G.LOGGING.debug(repr(err))
        return None
    else:
        return ret

以TemplateMatching類的find_best_result()為例,看一下內部邏輯如何實現。

@print_run_time
def find_best_result(self):
    """基於kaze進行圖像識別,只篩選出最優區域."""
    """函數功能:找到最優結果."""
    # 第一步:校驗圖像輸入
    check_source_larger_than_search(self.im_source, self.im_search)
    # 第二步:計算模板匹配的結果矩陣res
    res = self._get_template_result_matrix()
    # 第三步:依次獲取匹配結果
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    h, w = self.im_search.shape[:2]
    # 求取可信度:
    confidence = self._get_confidence_from_matrix(max_loc, max_val, w, h)
    # 求取識別位置: 目標中心 + 目標區域:
    middle_point, rectangle = self._get_target_rectangle(max_loc, w, h)
    best_match = generate_result(middle_point, rectangle, confidence)
    LOGGING.debug("[%s] threshold=%s, result=%s" % (self.METHOD_NAME, self.threshold, best_match))
    return best_match if confidence >= self.threshold else Non

重點看第二步:計算模板匹配的結果矩陣res,res = self._get_template_result_matrix()

def _get_template_result_matrix(self):
    """求取模板匹配的結果矩陣."""
    # 灰度識別: cv2.matchTemplate( )只能處理灰度圖片參數
    s_gray, i_gray = img_mat_rgb_2_gray(self.im_search), img_mat_rgb_2_gray(self.im_source)
    return cv2.matchTemplate(i_gray, s_gray, cv2.TM_CCOEFF_NORMED)

可以看到最終用的是openCV的方法,cv2.matchTemplate,那個優先匹配上就返回結果。

4 總結

使用過程中可以發現Airtest框架有兩個缺點:一是對於背景透明的按鈕或者控制項,識別難度大;二是無法獲取文本內容,但這一缺點可通過引入文字識別庫解決,如:pytesseract。
對不能用UI控制項定位的部件,使用圖像識別定位還是非常方便的。UI自動化腳本編寫過程中可以將幾個框架結合使用,uiautomator定位速度較快,但對於flutter語言寫的頁面經常有一些部件無法定位,此時可以引入airtest框架用圖片進行定位。每個框架都有優劣勢,組合使用才能更好的實現目的。

作者:京東物流 範文君

來源:京東雲開發者社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # vi命令使用詳解 ### 1. 三種工作模式 1. 命令模式:通過**命令**對文件進行常規操作 * 打開文件時進入命令模式 **(vi的入口)** * 通過命令對文件進行常規操作,如定位、翻頁、複製、粘貼、刪除等在圖形界面下通過滑鼠或快捷鍵實現的的操作 2. 末行模式:執行**保存、退出**等 ...
  • 大家好,我是 god23bin。歡迎來到《一分鐘學一個 Linux 命令》系列,每天只需一分鐘,記住一個 Linux 命令不成問題。今天要說的是 ps 命令。 ...
  • MVCC併發版本控制 本文大部分來自《MySQL是怎樣運行的》,這裡只是簡單總結,用於各位回憶和複習。 版本鏈 對於使用 InnoDB 存儲引擎的表來說,它的聚簇索引記錄中都包含兩個必要的隱藏列(不知道的快去看《MySQL是怎樣運行的》) trx_id :每次一個事務對某條聚簇索引記錄進行改動時,都 ...
  • Linux安裝MongoDB 4.0.3 1.準備 CentOS下安裝MongoDB 官網提供windows、Linux、OSX系統環境下的安裝包,這裡主要是記錄一下在Linux下的安裝。首先到官網下載安裝包。文中安裝的是4.0.3版本的。 官網地址:https://www.mongodb.com/ ...
  • 近日,華為全球智慧金融峰會2023在上海順利舉行,華為雲副總裁、戰略與產業發展部總裁黃瑾發表了《做強堅實數據底座,GaussDB與產業攜手共進》的主題演講。 以下為演講實錄: 尊敬的各位來賓,大家下午好!非常高興和大家探討關於做堅實數據底座,GaussDB與產業攜手共進的一些思考。 中國資料庫市場發 ...
  • #報錯信息: ``` ****: 第4 行附近出現錯誤: 不是 GROUP BY 表達式 ``` #修改辦法: ######達夢可以配置相容參數,COMPATIBLE_MODE=4,靜態參數,需要重啟資料庫後生效! ``` sp_set_para_value(2,'COMPATIBLE_MODE', ...
  • 本文主要測試驗證 Elasticsearch 各版本快照在 [Easysearch](https://www.infinilabs.com/docs/latest/easysearch/overview) 中進行數據恢復。 ## 準備測試數據 ### 索引 ![](https://www.infin ...
  • # 一、Android程式員需要具備的素養 1. 應該熱愛學習Android知識 2. 具備基本的自學能力和解決問題的能力 3. 具備實踐能力 # 二、Android程式員最終需要熟練掌握的語言 1. Java(基本) 2. C/C++(進階) 3. Kotlin(基本) 4. Python(可選) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...