Qunar 基於自動錄製的 Tars 系統演進
作者介紹
崔宇
2018 年加入去哪兒網,目前擔任機票客戶端開發負責人,擅長 ios、android 以及 RN 技術,主導了 TARS-UI 自動化測試系統的開發和落地使用。
一、前言
去哪兒旅行 UI 自動化系統 TARS 於 2020 年 3 月啓動,到 2020 年底達成一期預計目標。覆蓋公共、機票、酒店等多個部門接入,月版發版 30 多次,迴歸執行自動化上百次,200 多個項目發佈執行 400 多次,發版無 P1、P2 故障,節省人力近千 pd 。
持續運行一段時間後,TARS 團隊收集了運行中的各種問題,並制定解決方案,啓動了新一輪的迭代設計。
二、TARS 核心指標和問題
要洞察到 TARS 系統有哪些問題,首先要有一個合理的評價標準。在 TARS 系統運行過程中,我們總結了五個主要指標來體現 UI 自動化的執行情況。
- 覆蓋度
-
包括業務線覆蓋度與 case 覆蓋度。
-
業務線覆蓋度代表是否覆蓋了公司所有業務線。
-
case 覆蓋度指每個業務線迴歸 case 的覆蓋程度。
- 攔截跳過率
-
我們對前端項目的發版進行了 UI 自動化攔截,必須通過自動化才能發佈。
-
在某些情況下,如果不能跑自動化,可以申請跳過。
-
攔截跳過率代表發佈項目中沒有跑自動化任務的比例。綜合反應了 TARS 系統是否好用。
- 準確率
假如一個自動化任務跑 100 個 case,通過的有 90 個,未通過的有 10 個。但未通過的 10 個不一定都是 bug 導致的,可能有 5 個是 case 不完善導致的,有 4 個是系統不穩定導致的,只有 1 個是真正的 bug,那麼就認爲準確率爲 91% 。
- 運行時長
- 一個自動化任務平均運行時間。
- bug 召回率
假如要發佈的項目有 10 個 bug,通過自動化測試測出來 3 個,那麼召回率就是 30% 。
通過對這些核心指標的持續關注,我們發現了以下幾個問題:
1. 覆蓋度低,新業務線接入很少,case 更新也不頻繁。 分析原因,主要是因爲老的系統接入成本太高,需要 QA 掌握很多 UI 自動化運行環境的知識。而且 case 編寫成本比較高,大家添加新 case 的意願比較低。case 覆蓋度低的話,那麼就不能覆蓋足夠多的業務場景,bug 召回率就會降低。
2. 攔截跳過率高。 分析原因,是因爲每一次自動化任務執行時間比較長,而且由於系統原因造成的 case 失敗率高,經常誤報不通過。操作體驗也比較差,同學們爲了儘快發佈,經常會選擇跳過自動化任務。
3. 準確率不到 80%。 分析原因有幾個方面的問題。首先是,使用線上數據進行測試,由於業務一直在迭代,使用線上數據經常會遇到 case 沒有覆蓋到的操作,比如新彈窗等。第二,人工編寫的 case 質量參差不齊,這也決定了 case 是否健壯穩定。第三,自動化框架不穩定經常會造成手機控制失敗,case 運行不起來等問題導致 case 執行失敗。
4. 每個任務運行時間比較長。 分析原因,case 失敗導致重試的次數多以及設備沒有充分被利用是主要原因。
如何解決以上問題,就是我們本次重構的關鍵。總結下來,我們要解決的問題如下:
-
case 編寫難度高,線上數據變化多,case 不容易覆蓋各種情況;
-
系統環境接入複雜,而且穩定性差;
-
任務執行時間長,設備沒有被充分利用;
-
平臺操作體驗需要提升
接下來,我們先來看看如何解決第一個問題。
三、自動錄製 case 方案
1、UI 自動化技術簡介
UI 自動化的核心邏輯就是通過模擬用戶的操作來實現自動化測試。模擬用戶操作的關鍵技術就是如何查找頁面元素。目前查找頁面元素的方式大致有兩種:圖像識別和元素標識。
- 圖像識別
圖像識別是脫離頁面 UI 組成結構,模擬人眼看圖的邏輯來識別元素。比較流行的方案有圖像比對、文字識別、AI 技術識別等。這種方式的優點是不受程序實現方式與系統的限制,能夠更加通用可靠的實現查找元素。缺點是對元素含義的識別比較困難,容易查找到錯誤的元素。
- 元素標識
元素識別是基於代碼結構,通過界面上的元素標識、path 路徑、文字等方式來定位一個頁面元素。這種方式的優點是能夠比較準確的找到業務需要的元素,缺點是受限於程序的實現方式和系統的限制,有時候獲取元素標識會不穩定,給元素加標識工作量比較大。
雖然工作量會大一點,但是通過元素標識的方式來查找頁面元素目前對我們來說是更可行可靠的方案,所以我們是基於元素標識的方式來實現自動化 case 的錄製回放。
就元素標識識別的方式來說,它在實踐的過程中也經歷了一些發展演變:
2、TARS 編寫 case 方式
TARS 編寫自動化測試 case 是基於 POM 原理。過程如下圖:
首先,我們先手動在代碼中給要操作的元素添加標籤,比如 com.Qunar:id/ato_flight_tv_dep_city
第二,爲了避免代碼迭代對 case 維護造成影響,我們在 case 腳本中將標籤封裝成了元素組件 arr_city,這樣,即使界面結構發生了變化,只要該元素還存在,就仍然能準確找到這個元素,並不需要經常維護。
第三,然後我們封裝了基於元素的操作行爲,比如選擇到達城市。在實際編寫 case 的時候,QA 同學就可以直接調用交互方法來組成一個 case 操作流程。
第四,在執行 case 的時候,我們會通過數據工具拉取線上符合我們 case 要求的數據環境,然後在數據環境的指導下操作 APP 執行 case。
第五,最終 case 腳本按照固定的步驟執行,如果元素標籤發生了變化或者元素功能需要維護,只需要修改對應的標籤或方法即可,並不需要對 case 腳本進行修改。
這種方式已經非常好的解決了腳本編寫工作量大,維護成本高的問題。但是在實際落地過程中,QA 人員仍然會因爲 case 編寫難度高而很少新增新的 case。
爲了思考如何更進一步的降低 case 編寫成本,我們將編寫 case 核心功能點抽象如下:
抽象出來編寫 case 的核心功能點之後,我們發現了一些問題:
-
手動維護 UI 元素標籤:標籤維護工作量大,每次有標籤的修改還要重新發布版本,而且人工編寫容易出錯。
-
case 編寫學習門檻高:雖然我們已經很大程度上對頁面的交互進行了封裝,QA 同學只需要調用即可,但是總會遇到未封裝的行爲,對於 QA 同學的編碼設計能力有一定的要求。
-
使用線上數據執行 case:線上數據經常會出現變化,需要不斷的維護新增的預期外的流程。而且容易造成 case 執行的不穩定。
這些問題就造成了 QA 覺得編寫 case 是一種負擔,導致了 case 覆蓋度不夠,進一步導致召回率低。使用線上數據執行以及 case 編寫質量參差不齊造成了準確率的降低。case 執行的準確率低就會造成 case 重試次數增多,導致執行時間長,然後就影響了攔截跳過率。
所以我們需要思考能不能使用自動生成標籤、自動生成腳本、使用穩定的數據來執行 case 的自動錄製 case 的方式來編寫 case。
3、如何自動錄製 case
能夠自動添加標籤、自動編寫腳本,這種 case 錄製方式我們叫它 “自動錄製 case”。
理論上,如果程序代碼一樣、數據一樣的話,一樣的操作就能復現一樣的結果。
在這個基礎上,如果用最新開發的代碼進行回放,數據一樣,操作一樣,但是結果不一樣,那就可能是兩個原因,一個是 case 需要維護,一個是代碼寫錯了。
case 需要維護的情況一般來說是相關功能進行了改版,老 case 不適用於新業務。這種情況肯定是要維護 case 的。
自動化測試的意義在於將重複的勞動自動化執行。也就是說,是對老功能進行迴歸,而不是對新功能進行測試,所以由於新功能的上線造成一些老 case 被廢棄是必然的。
那麼把這部分 case 去掉,其他的沒有執行通過的 case,大概率就是測出 bug 了,所以用錄製回放的機制來作爲自動化測試的 case 是可行的。
想要把用戶的操作行爲記錄下來進行回放,首先要解決如何記錄元素的問題。也就是自動生成元素唯一標籤,通過唯一標籤來定位元素。
這種方式看上去和基於絕對路徑的 case 錄製很像,因爲之所以使用絕對路徑生成 case,就是因爲它是天然的元素唯一標識,但是這樣我們的元素標識就會非常不穩定。
稍微有一點變化就會導致找不到以前的標識。那麼如何既能夠自動生成元素標籤,又可以一定程度上比較穩定呢?
-
自動添加標籤
首先我們要思考的是標籤生成的規範是什麼?經過我們的思考,主要有兩點:
-
頁面唯一。標籤必須是唯一的,不然可能回放 case 的時候會點錯元素,導致最後測試結果有誤。
-
一定程度上能夠抵抗 DOM 樹的變化,不會因爲業務迭代而發生頻繁的標籤改變。
經過研究,我們認爲用組件樹的方式來生成標籤比較符合要求。
首先,一個頁面的元素在我們的代碼中是有一個組件層級歸屬關係的。比如之前到達城市那個按鈕,它的索引關係爲機票首頁 - 搜索面板 - 單程面板 - 到達城市,一般來說,我們的代碼都是會按照頁面、模塊、組件這樣的方式來進行組織。這就天然形成了一種標籤的規則,就是按照組件層級進行唯一定位。
但是在實際操作中有一些特例。比如,某些元素是通過 for 循環來生成的,這樣每一個元素標識都是一樣的,於是我們就追加每一個元素的 key 來唯一標識。還有可能在一個代碼文件中同一個元素寫在了多個地方,這種情況我們就用元素所在的代碼行數來唯一標識。
這樣,我們生成的標籤大概是這個樣子的:
flight|OrderFillView|OrderFillPage_NewPassengerCard_ListPassengers_ToAddPassengerListView_ToAddPassengerItemView|TouchableOpacity_line=110
可以看到,這個標籤可以清晰的標識出元素的業務線、頁面、業務模塊、組件類型。
標籤規則定好了,我們就可以按照這個規則自動生成元素標籤。爲了避免人爲破壞標籤,我們選擇在代碼編譯期通過插件的方式將標籤信息注入到相關的元素組件上。這樣,開發人員不會在代碼中看到他們,避免了人爲修改的維護問題。
標籤自動生成一定是基於相關代碼版本的,一旦代碼發生了變化,很可能重新生成的標籤和上一次的標籤不一樣。這樣我們基於上一次標籤版本錄製的 case 就跑不了了,因爲元素都找不到了。
那麼對於組件層級生成的標籤,是否會因爲代碼的改變而經常需要維護?
一般如果只是簡單的增加新功能,一般是不會破壞組件層級的。而列表項的唯一標識是與數據相關,只要數據不變,key 就不會變。
那麼現在就剩下 line 標識可能會經常需要維護了。
-
自動維護標籤
根據之前的分析,我們對於通過 line 標識來進行唯一性區分的標籤可能會頻繁的發生變化,導致 case 執行失敗。
所以對於這種情況,我們需要儘量通過自動維護標籤的方式來避免增加人工維護的工作量。
那麼我們能否識別出新老版本的標籤對應關係,將他們進行一致性關聯?
我們先梳理一下代碼變化對標籤產生的影響可能有那些。
第一種情況是標籤所在的元素組件代碼沒有修改,但是代碼文件其他地方產生了變化。
第二種是標籤所在的組件被刪除。
第三種是標籤所在的組件被修改。
對於第二種和第三種,我們肯定是要人工對 case 進行維護,因爲這種情況屬於業務本身產生了變化,以前的 case 不適用了。
而第一種情況是我們需要進行自動關聯的情況。我們可以通過 diff 的方式來識別新標籤以前是哪一個標籤,然後在跑 case 之前先進行標籤的關聯,然後再跑 case。
-
記錄 QA 操作自動生成 case 腳本
所謂記錄就是要在可交互元素觸發操作的時候,記錄下來哪個元素在哪個時間被做了什麼操作。所以,我們需要攔截元素的事件回調方法,先記錄埋點,再調用以前的邏輯。
我們通過裝飾器模式,在編譯期對可交互元素進行包裝,這樣,QA 操作元素的時候就會先記錄埋點。
埋點信息裏面包含元素標籤、業務線、所在頁面、所在模塊、操作時間、操作類型、代碼版本等信息。
然後在錄製 case 的時候,我們將埋點數據保存下來,形成用戶操作列表。case 即是將這個操作列表的操作翻譯成自動化腳本進行模擬點擊。
-
保存數據現場
現在我們自動將 QA 的操作記錄了下來生成了自動化腳本,同時還需要將數據現場進行保存,不然我們就無法正確的回放 case。
在 QA 操作 APP 的時候,我們會將每一個接口請求的請求數據和返回數據進行存儲,當回放的時候,我們按順序使用本地的接口數據替換每一個接口請求的返回值,也就是 mock 。
這樣,就可以保證 case 腳本所操作元素都在,除非代碼進行了迭代。而且使用 mock 數據,避免了複雜的線上數據場景,增強了 case 執行的穩定性。
這樣,我們錄製 case 就是將 QA 的操作和數據存儲下來,執行 case 就是用這個數據在被測代碼中進行回放。
-
自動生成斷言
一個 case,要有一個結果來判定是否通過。一般來講,我們是通過斷言來判定 case 最後是否執行成功。我們來看一個例子:
首頁 -> 商品列表 -> 選擇商品 -> 商品詳情 -> 購買 -> 聯繫人 -> 進入確認訂單頁
當上面這個 case 執行完之後,我們需要判斷,是否確定 “進入了確認訂單頁”。這個操作我們當然也不能手寫。
爲此,我們與算法組合作,引入了 “智能 OCR 識別” 功能。普通的 OCR 識別是匹配頁面上的文字,但是對於我們的場景來說還不能完全滿足,因爲可能頁面上有多個一樣的文字。所以我們還需要知道文字所在的上下文,比如位置,相關的其他文案等。
“智能 OCR 識別” 功能可以先將頁面模塊進行劃分,然後識別每一個模塊中我們要查找的文字,然後返回文字所在模塊的所有信息。這樣,我們就可以精準定義我們的斷言條件了。
有了 “智能 OCR 識別”,我們可以將斷言寫成一個條件,作爲 case 的一個屬性信息一起保存在 case 數據中。不需要手動編寫代碼腳本來判斷。
業務邏輯的斷言驗證完還不算 case 真正通過,我們還需要驗證數據的斷言。
因爲我們使用的 mock 數據來跑 case,即是我們代碼寫錯將請求參數拼錯了,也不會影響數據返回結果。所以如果出現了這種 bug,我們就感知不到了。所以,我們需要對被測代碼中的請求數據進行校驗。
由於我們保存了接口的請求數據,理論上如果跑 case 的時候,生成的請求數據和保存的請求數據完全一致,那肯定是沒問題的。但是由於業務迭代,請求數據一般會發生變化,所以我們要比對的是關鍵的數據節點。關鍵數據節點前後完全一致,就可以認爲請求數據的邏輯是對的。
業務斷言和數據斷言都通過,一個 case 就算是通過了。
-
改造後的流程
自動錄製 case 的機制完成以後,流程變成了下圖樣子,基本上完全實現了自動化,降低了 case 編寫的難度:
錄製完 case 之後,只需要在平臺上進行上傳即可。
自動錄製 case 解決了 case 編寫難的問題,也一定程度上提高了自動化任務的準確率。下面我們來看看如何提升框架穩定性,降低接入成本,充分利用設備,優化操作體驗。
四、全新的自動化平臺
第一個問題解決了,然後爲了解決其他的問題,我們也對整個自動化系統進行了升級,包括基礎設施、調度平臺、報告等。
1、TARS 基礎設施升級
升級前,我們的底層系統架構如下:
改造後的系統如下:
2、分佈式設備調度
以前,我們是每一個業務線自己維護一套設備。由於不同業務線執行自動化任務的頻率不一樣,就造成了設備使用上的浪費,有大量的手機一直在閒置。
爲了解決這個問題,我們基於 STF 打造了分佈式的設備調度系統。更全面的覆蓋了手機設備型號,重複利用設備能力,由更靈活的設備調度策略來管理 case 運行。
3、TARS 雲測平臺
爲了進一步提升用戶體驗,滿足開發或 QA 同學對自動化平臺的各種需求,我們打造了全新的自動化平臺,提供了豐富的功能,優化了日常使用的效率。
雲測平臺不僅僅能夠支持日常的 case 任務執行,還能支持遠程真機租用、性能等專項測試、行爲回溯等功能,爲大家提供更多基於自動化技術的服務。
五、總結規劃
升級改造後的平臺各項指標都達到了預期的目標。
-
業務線接入非常方便,錄製 case 也很方便
-
攔截跳過率降到了 8% 以內
-
準確率達到了 95% 以上
-
平均一個自動化任務執行時間在 10 分鐘以內
-
提升了自動化覆蓋度
但是在實際場景中,自動錄製 case 並不能完全取代手動編寫 case。因爲它需要用 mock 數據進行測試,也就是說,對於需要使用線上數據進行測試的場景就無能爲力了。
所以手動編寫 case 本身還需要進一步的提升體驗,比如未來我們會通過強大的 IDE 功能來簡化 case 編寫操作,也可能會藉助 AI 來幫助處理一些廣告彈窗等 case 未覆蓋的問題。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/2xe9t4roXgY5fJHVvpakSg