唯品會iOS程式碼覆蓋率的應用實踐

唯技術訂閱號發表於2019-02-25

 背    景 

     唯品會特賣會App承載著前端選購、下單、支付等核心業務,業務邏輯異常複雜,以iOS端為例,程式碼行數已達50萬。2018年初,我們啟動了研發流程的最佳化,大版本迭代週期由3周縮減至2周,伴隨而來的是測試獨佔時長的壓縮。如何在更短的測試時間內保證App程式碼質量是對測試團隊一次挑戰。長期以來,黑盒測試依賴於測試人員的能力和業務熟悉度,測試質量的高低沒有客觀資料可衡量。推進精準測試是我們的必經之路,而程式碼覆蓋率是必須要跨過的檻,有見及此,特賣會App團隊著手研究iOS及Android程式碼覆蓋率的應用落地。本文重點講解iOS程式碼覆蓋率的應用實踐。


調研中發現,iOS程式碼覆蓋率如要大規模應用到功能/迴歸測試,需解決如下三個痛點:

  • 蘋果開發工具Xcode僅支援單元測試的程式碼覆蓋率,而且只能在本地連線除錯。而實際的測試裝置多達20+,把裝置連線電腦再收集覆蓋率是不可行的,我們必須做到收集自動化,而且對使用者無感知。

  • App的功能分支測試周期大約有4天,此期間可能存在多次的開發程式碼提交、缺陷修復,以哪一個版本作為覆蓋率統計是值得思考的。我們嘗試約定在某個穩定版本做全面的功能測試,但效果並不好,因為前期已測過的功能沒必要重測一遍,時間也不允許。因此,我們需要支援行覆蓋率的多版本合併。

  • 鑑於app的執行模式僅支援離線插樁,我們需要處理大量手機使用者,不同時間節點的覆蓋率資料。


 技術選型 

首先,程式碼覆蓋率要考慮插樁,插樁方式有3種:1)原始碼插樁;2)中間程式碼插樁;3)可執行檔案的二進位制插樁。iOS使用clang作為前端編譯器,負責生成AST語法樹,並將程式碼編譯成LLVM bitcode;而LLVM作為後端編譯器負責生成平臺相關的機器語言。其編譯插樁過程屬於中間程式碼插樁,簡化的過程可參考圖1。LLVM脫胎於GCC,也可使用gcda/gcno記錄程式碼覆蓋率。其中:

  • gcno是notes file,插樁編譯後生成;

  • gcda是data file,與gcno對應,由程式執行時記錄;

    LCOV作為GCC Code Coverage的開源前端工具,支援行/函式/分支覆蓋的報告輸出。因此,我們使用LCOV作為iOS程式碼覆蓋率生成工具。

唯品會iOS程式碼覆蓋率的應用實踐

圖1: 插樁編譯原理


iOS覆蓋率工具整體框架如下:

唯品會iOS程式碼覆蓋率的應用實踐

圖2: 系統架構

  • 前端UI接入公司的覆蓋率平臺,支援覆蓋率的收集、生成報告、郵件通知;

  • 排程層負責分發訊息至具體執行的機器AnalyserAnalyserjenkins封裝,節點的機器必須為Mac系統,物理機或虛擬機器均可;

  • 分析器支援覆蓋率檔案的合併、差異以及報告的輸出。另外分析器負責接收終端上報覆蓋率資料;

  • 檔案系統管理覆蓋率檔案的中間資料以及版本構建資訊

 實現原理 

我們設計出一整套解決方案,請看圖3

唯品會iOS程式碼覆蓋率的應用實踐圖3:iOS程式碼覆蓋率實現時序圖

具體處理時序如下:

1) Xcode打包伺服器在CI中自動插樁,並根據commit id,上傳與之對應的標籤檔案gcno;

2) 使用者透過OTA方式安裝app,在測試過程中只需把app置於後臺,覆蓋率資料即可從快取刷入硬碟,並透過http介面上傳至檔案伺服器;

3) 覆蓋率平臺可通手工觸發方式,收集指定分支、commit id的覆蓋率檔案info;

4) 待預處理完畢後,覆蓋率平臺可生成合並的或差異的覆蓋率報告;

接下來我們從如下三方面剖析技術實現:

  • Xcode插樁

  • Pod上傳伺服器

  • 覆蓋率檔案的合併/差異處理

(一)    Xcode插樁

在BuildSettings分別設定Instrument Program Flow、Generate Legacy Test Coverage File為True,即可開啟插樁。

唯品會iOS程式碼覆蓋率的應用實踐


圖4: Xcode 插樁設定

(二)    Pod上傳伺服器

透過Pod的外掛方式,我們使用C++混編,呼叫外部的__gcov_flush方法把覆蓋率gcda從記憶體刷到硬碟。為了方便交叉編譯,GCC設計了兩個與生成路徑相關的變數GCOV_PREFIX,GCOV_PREFIX_STRIP,如下圖。

唯品會iOS程式碼覆蓋率的應用實踐

圖5: Pod的外掛實現

在生成GCDA後就要考慮上傳到檔案伺服器了,我們採用AFNETWORKING庫,把多個GCDA檔案整合在一個介面上傳,檔案大小約7M Bytes

(三)    覆蓋率檔案預處理,支援差異/多版本合併

我們使用開源LCOV來處理檔案,gcda+gcno+原始碼可生成中間檔案info,info檔案包含了最終呈現報告的所有資訊,包括原始碼路徑、函式名、函式執行次數、函式總數、函式在原始檔的位置、行號、行執行次數、行總數。為支援行差異覆蓋率,我們自定義了新欄位CA/CF/CH,請見圖6。


唯品會iOS程式碼覆蓋率的應用實踐

圖6:  中間檔案info資料結構

對於兩個不同版本的合併,使用平移。原則是把舊版本程式碼裡的未變更的行覆蓋平移到新版本。請見圖7。

唯品會iOS程式碼覆蓋率的應用實踐

圖7: 新舊版本git diff對比

具體流程如下,舊程式碼的覆蓋率與git diff 比較,先判斷是否塊內程式碼,如不是塊內程式碼則平移行號至新版本;若是塊內程式碼,則再判斷是否為已刪除的行號,若是,則跳過不做平移;若不是已刪除的行,則平移行號,請見圖8。或許讀者有疑問,兩個版本的上下文都變了,平移行覆蓋率後的可信度高麼?筆者想強調的是,覆蓋率報告好比一份健康檢查報告,只關心異常點,沒有覆蓋的行就是異常點。在使用平移演算法後那些依然沒有覆蓋的行就是沒經探索的荒地,將是重點關注的物件。

唯品會iOS程式碼覆蓋率的應用實踐

圖8: 兩個版本的行覆蓋率合併流程

我們再擴充套件至n個版本的合併,可沿用以上的平移演算法,只需把最後一個版本作為平移的基準版本即可,可參考下圖。

唯品會iOS程式碼覆蓋率的應用實踐

圖9:  多版本的行覆蓋率合併流程

至此,iOS覆蓋率整套實現方案描述完畢。

 實踐效果 

iOS覆蓋率工具已接入至覆蓋率平臺,可透過伺服器管理指定待收集的系統平臺,分支;平臺支援多分支同時收集。功能測試分支的覆蓋率收集時間可控制在20分鐘內。覆蓋率收集完成後,平臺可設定行差異覆蓋率的對比基線,可設定一個分支內合併的多個版本,效果請見圖10、11、12。

唯品會iOS程式碼覆蓋率的應用實踐

圖10:  移動測試列表伺服器管理

唯品會iOS程式碼覆蓋率的應用實踐

圖11:  生成覆蓋率報告頁面

唯品會iOS程式碼覆蓋率的應用實踐

圖12:  iOS覆蓋率報告的目錄結構

最後,我們看看該工具的應用情況,特賣會App當前每兩週發一個版本,在功能測試階段,各條業務線(包括特賣/基礎/使用者/創新)已開始使用iOS覆蓋率檢測工具,從使用的經驗看,功能測試階段的程式碼行差異覆蓋率達到90%,未覆蓋的行主要包括三項,風險可控:

  • 異常捕捉

  • 非空指標的保護

  • 冗餘預留程式碼

 精準測試方法總結 

結合iOS覆蓋率工具,我們總結出目前適合特賣會app的精準測試策略,請見圖13。在測試分析階段,我們注重需求差異,技術差異的分析,結合用例地圖得出測試策略,並指導我們進行冒煙/功能測試,每個測試階段的Code Review著重點有所不同,在冒煙測試階段,CR著重檢查開關、介面名、介面引數、model欄位;在功能測試階段,CR重點關注介面異常/跳轉、行為;迴歸階段則優先檢查Bug Fix的程式碼改動。測試階段均透過行差異覆蓋率檢查測試遺漏,並反饋更新測試策略。

唯品會iOS程式碼覆蓋率的應用實踐

圖13: 特賣會App精準測試策略

 下一步計劃 

目前使用的人工分析+工具輔助的方式進行精準測試,在這條探索的道路上我們還有很長的路要走。人工分析受制於技術,需求背景,能力和經驗,我們很難保證質量。如何把已有的覆蓋率資料轉化為事前分析的參考,並結合整理的用例-模組(檔案、函式、分支)對映庫實行自助分析、測試用例集推薦,將是我們下一步研究的重點。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900365/viewspace-2636946/,如需轉載,請註明出處,否則將追究法律責任。

相關文章