馬蜂窩技術原創內容,更多干貨請關注公眾號:mfwtech
測試覆蓋率常被用來衡量測試的充分性和完整性,也是測試有效性的一個度量。「敏捷開發」的大潮之下,如何在快速迭代的同時保證對被測代碼的覆蓋度和產品質量,是一個非常有挑戰性的話題。
在馬蜂窩大交通、酒店等交易相關業務中,項目的開發和測試實踐同樣遵循敏捷的原則,迭代周期短、速度快。因此,如何依據測試覆蓋率數據幫助我們有效判斷項目質量、了解測試狀態、提升迭代效率,是我們一直很重視的工作。
Part.1 測試覆蓋率統計中的挑戰
對於功能測試而言,通常可以通過充分了解需求、完善的測試用例、接口測試、Review 技術方案等來保證測試充分性。但隨着業務規模快速發展,業務邏輯越來越復雜,系統級別交互越來越多,這些方法都不能保證所有的代碼一定被全部測試過,也給測試人員帶來極大挑戰。
說到這兒和大家分享一個因為測試覆蓋不充分,影響到線上業務的真實案例。事件起因是項目提測階段一個微服務 Sonar 掃描沒通過,開發同學為了修復 Sonar 發現的問題而重構了一部分歷史代碼,卻導致一個原有發券需求的錯誤。當天下午運營觸發發券后 Bug 出現直接導致生單不可用,並且持續了將近一個小時。這里的問題就是發券功能與此次需求無關,開發人員修改代碼后測試人員不知道,也沒有經過測試,但跟隨本次需求一起上線了,測試用例無法覆蓋到。
通過這個例子可以明顯地看到正確統計被測代碼覆蓋率的重要性。當前市面上統計覆蓋率的工具很多,但應用到我們的實際場景中通常存在以下問題:
-
大部分第三方工具都是以支持提測前單元測試的覆蓋率統計為主,而支持提測后測試覆蓋率的工具則比較缺乏;
-
第三方工具提供的覆蓋率基本都是展示全量的,但大部分情況下,測試人員重點關注的應該是本次新增和修改的部分,而不是耗費過多精力在未修改的部分上;
-
為了支持多需求並行測試,大交通后端服務采用 QA 隔離環境,而市面上還沒有支持多需求隔離環境並行測試的測試覆蓋率統計工具。
因此,大交通測試組決定自研工具進行被測代碼的覆蓋率統計,用代碼覆蓋結果反向地檢查測試人員測試用例的覆蓋度和開發人員代碼的冗余度,更精准地定位和分析問題,保證產品質量。
Part.2 覆蓋率統計工具設計
結合我們的實際場景,覆蓋率統計工具的實現目標如下:
-
大交通業務后端代碼使用 Java 語言開發,主要的業務處理邏輯都在后端,所以工具要優先支持 Java 代碼覆蓋率統計
-
可以統計提測后測試過程中不同類型測試的覆蓋率,包括:手工測試、回歸測試、接口測試、UI 自動化測試等等
-
支持全量、增量覆蓋率統計
-
支持多需求多環境並行測試,同時統計覆蓋率
-
用戶不局限於測試人員,而是所有有測試需求的人員,包括開發和測試等
-
支持自動化統計,簡化操作步驟,無需學習成本,報告簡單易懂
我們將覆蓋率統計工具命名為 jCover,並將其引入 CI/CD 體系,與內部 Java 平台項目管理及持續集成系統 MONE 打通。工具設計如下圖所示:

圖 1:覆蓋率工具整體架構圖
覆蓋率統計的大致流程為首先通過獲取多環境服務的配置信息,生成指定環境服務的測試覆蓋率文件,然后根據需要生成全量覆蓋率報告和增量覆蓋率報告,最后,存儲測試覆蓋率報告到指定環境目錄下。
Part.3 主要功能及實現方式
jCover 的主要功能包括:
-
支持多需求多環境並行測試下,同時統計覆蓋率
-
支持全量覆蓋率統計
-
支持增量覆蓋率統計
-
支持自動/手動統計覆蓋率
-
覆蓋率報告統一管理
下面圍繞以上功能點,為大家介紹 jCover 的作用、特點和具體實現方式。
1. 覆蓋率工具平台化
為了簡化操作步驟降低學習成本、報告簡單易懂,jCover 支持界面化操作,可以手動生成測試覆蓋率報告和查詢測試覆蓋率報告;查詢測試覆蓋率報告入口統一,代碼測試覆蓋率數據清晰,方便數據統計和量化。

圖 2:覆蓋率報告查詢頁面
2. 支持多需求多環境下的並行測試
由於使用敏捷迭代,項目多、並行率高,大交通所有微服務均引入測試環境隔離插件,同時支持多項目並行測試。因此,jCover 需要支持多環境並行的測試覆蓋率統計。
具體來說,每個測試項目都會有一個單獨的環境標識,以隔離環境標識+環境類型+服務名稱唯一確定一個覆蓋率統計單元;每一個覆蓋率統計單元都會生成對應的測試覆蓋率統計報告,測試覆蓋率統計報告被存儲到 jCover 部署的服務器中,存儲位置是環境標識對應的服務目錄下;同一個服務的不同提測分支同時在多項目中測試時相互間的覆蓋率報告不會被覆蓋。
多環境並行流程圖見下圖(目前只支持 QA 環境覆蓋率統計):

圖 3:多環境並行流程圖
例如:隔離環境 gjssqz 和隔離環境 supplyrefund 可以同時進行測試覆蓋率統計,隔離環境標識+環境類型+服務名稱指定了每個環境下的單個服務測試覆蓋率統計結果。
覆蓋率工具 UI 展示如下圖:

圖 4:用「隔離環境標識+環境類型+服務名稱」來標識和統計覆蓋率
3. 全量覆蓋率統計
在測試時有時候需要關注全量代碼的測試覆蓋率,代碼全量覆蓋率統計過程如下:
(1)查詢服務信息
jCover 通過內部 Java 平台項目管理及持續集成系統 MONE 來查詢被測微服務的信息:
-
為了減輕對 MONE 系統的壓力,設定每日一次定時拉取全量服務信息,將隔離環境標識、服務名稱、容器 IP、監聽端口、環境類型和狀態存儲到數據庫。
-
生成覆蓋率報告時根據隔離環境標識和服務名稱從 MONE 單服務信息拉取接口查詢最新的容器 IP。如果數據庫沒有就新增服務信息,如果數據庫有就更新服務對應 IP。
(2)收集覆蓋率數據
為了做覆蓋率統計,需要從微服務部署的容器中下載測試覆蓋率 Dump 文件。我們采用的解決方案是使用 JavaAgent 代理,首先對微服務部署的容器進行了改造,使得容器支持 JavaAgent 功能;其次為了動態插樁記錄被測代碼的運行結果,在被測微服務的 JVM 啟動腳本中增加 JavaAgent 參數。
覆蓋率工具根據 IP 和監聽端口通過 JavaAgent 代理從容器中下載 Dump 文件,以環境和服務標識唯一 Dump 文件,並存儲到 jCover 所在的服務器。在下載覆蓋率數據時采用追加方式,如果該 Dump 覆蓋率文件已存在,那么該輪的覆蓋率數據會直接在文本末尾進行追加,便於統計整個測試過程的代碼覆蓋結果。
(3)根據全量覆蓋率文件生成全量覆蓋率報告
-
MONE 從微服務容器中拷貝 Class 文件到 jCover 所在服務器指定目錄
-
jCover 通過 Class 文件和 Dump 文件生成全量覆蓋率文件
-
根據全量覆蓋率文件生成全量的測試覆蓋率報告,把全量的測試覆蓋率報告以「隔離環境標識+服務名稱+順序 ID」的維度存儲到 jCover 所在服務器的指定目錄
-
最后配置靜態資源映射規則生成測試覆蓋率報告 URL 並把測試覆蓋率報告 URL 存儲到數據庫中
用戶打開測試覆蓋率工具前端界面就可以在瀏覽器中查看步驟 D 生成的覆蓋率報告了,綠色表示被覆蓋,紅色表示未被覆蓋,黃色表示部分覆蓋:

圖 5:全量測試覆蓋率報告展示
4. 增量覆蓋率統計
業務發展到一定階段后,代碼量級很大。主干代碼我們默認是正確的代碼。當一個新需求提測后,測試人員更關注的其實是針對本次需求更改的代碼的覆蓋率而不是全部代碼的覆蓋率,想從全量覆蓋率報告中找出本次更改的增量代碼的覆蓋率是非常困難的。基於此問題我們開發了增量覆蓋率統計功能。
增量覆蓋率統計和全量覆蓋率統計有三個相同的步驟:查詢服務信息、收集覆蓋率數據、生成全量覆蓋率文件。生成全量覆蓋率文件后,增量覆蓋率統計的不同點是增加了以下處理:增量代碼獲取、生成增量覆蓋率報告,具體實現如下:
(1)Diff 增量代碼
-
分別獲取每個微服務的 master 主干和測試分支內容,Diff 測試分支對象和 Master 分支對象,得到增量代碼
-
濾除增量代碼中的非.java 文件
-
循環遍歷增量代碼得到變化行,標識行變化類型包括:新增、更新
(2)生成增量覆蓋率報告
-
根據增量代碼變化行標識無變化的方法
-
在改變的代碼行(改變包括增加和更新)行首加上對應的標識:「增加」的代碼以「+++」標識,「更新」的代碼以「U」標識,同時刪除無變化的方法,生成增量代碼覆蓋率報告

圖 6:增量覆蓋率流程
下圖是交易服務增量覆蓋率報告的截圖示例,從圖中清晰看出增量行和更新行的測試覆蓋率情況。

圖 7:增量覆蓋率報告展示
5. 支持手動/自動統計覆蓋率
當測試人員需要查看最新的測試覆蓋率時,可以通過「jCover - 生成測試覆蓋率報告」的入口手動觸發生成測試覆蓋率報告。

圖 8:手動統計覆蓋率圖形化界面
在一個需求的實際測試中被測的微服務通常有多個。因為修改 Bug 等原因,被測微服務經常需要重啟,重啟后容器銷毀,JavaAgent 插樁后的文件也會被刪除導致重啟前覆蓋的代碼無法被統計。
如果每次重啟每個被測微服務前都需要測試人員手動生成覆蓋率報告的話,是個很大的工作量也會經常被遺忘。因此 jCover 通過和發布系統結合實現了覆蓋率自動統計功能。具體實現方法為 jCover 提供了自動生成覆蓋率文件接口,在服務重啟部署時后端發布系統調用此接口,實現自動統計覆蓋率,不管服務重啟多少次都可以把每次測試時覆蓋的代碼全部統計到。
之前大交通的后端服務發布系統為 Jenkins,后續升級為了 MONE,兩種發布系統下均支持服務部署時自動統計覆蓋率。
Part.4 工具效果
經過一段時間推廣使用和優化,現在大交通所有的需求提測后均使用 jCover,主要體現出以下幾點優勢:
-
可以支撐不同類型的覆蓋率測試。jCover 可以統計手工測試、接口測試、回歸測試、UI 自動化測試等各種類型測試的全部覆蓋率,展示為一份覆蓋率報告。
-
及時發現漏測代碼。通過使用測試覆蓋率工具可以發現測試用例未覆蓋的代碼,反推測試用例缺失。例如在「虛艙管理」項目中國際交易支付前校驗失敗發送 MQ 消息沒有覆蓋,測試人員補充了支付前校驗失敗發消息的測試用例;在「國際搜索坑位供應商及價格優先級設置」項目中發現由於 Mock 數據的局限性, 帶有輔營的加價代碼未覆蓋,補充了機票+輔營的測試用例,提高測試覆蓋度。
-
發現開發的冗余代碼。例如:在「國際機票重復預訂優化項目」中發現冗余代碼,getBaseOrderDetail 方法無調用方,開發進行了刪除。
-
及時發現測試中的問題並改進,提高測試效率。在「國內供應商資源統一化」項目中測試人員執行了很多測試用例后,使用 jCover 查看覆蓋率報告發現應該覆蓋的代碼未被覆蓋,與開發溝通后發現控制這部分代碼邏輯的開關是「關閉」狀態,此開關應該是在「打開」狀態進行測試
-
為上線代碼審批提供數據參考。當團隊成員申請代碼上線時,開發 TL 可以重點關注未覆蓋部分代碼,減少線上問題發生。
-
輔助提升測試人員對被測代碼的熟悉度。
Part.5 近期規划
目前 jCover 只支持查看每一個類覆蓋行和未覆蓋行是什么,但是對於一個微服務整體行覆蓋率是多少沒有統計,我們正在開發增量覆蓋率百分比,幫助開發和測試做出准確的判斷。目前也在調研前端的覆蓋率統計,下一步將實現前端的覆蓋率統計工具 jCover。
在日常需求測試中可以采用測試(手工測試、接口測試、回歸測試等)+jCover 覆蓋分析的測試方法來完善測試場景,減少測試遺漏,確保測試的充分性,但需要注意的是 jCover 統計只能展示哪些代碼被覆蓋了,代碼的正確性還是需要用例的執行結果正確來保障;還有時候開發會漏開發某部分需求,此時依靠 jCover 是無法發現這部分遺漏代碼的,因此除了 jCover 之外可以通過參與技術評審、編寫用例時參考產品功能矩陣圖等多種手段發現問題,全方位保障被測代碼的質量。
以上就是對馬蜂窩大交通測試覆蓋率統計工具 jCover 的分享。當然,統計代碼覆蓋率僅僅是一種手段,覆蓋率高不一定代表質量好,但覆蓋率不高的代碼質量風險肯定很高。只有清楚在覆蓋率統計數據背后反映出的問題,才能從根本上保證軟件整體的質量。
本文作者:代春美,馬蜂窩測試部-交易測試工程師;孫海燕,馬蜂窩測試部-交易測試團隊負責人。

