本文作者:韓照光
來自公眾號:百度QA
☞背景☜
在當前web系統或app后端服務測試過程中, 黑盒測試占據了大部分的測試,即便是接口測試,也是基於場景的用例設計,這種測試方法完全依賴於測試人員的能力,經驗和業務熟悉度,而互聯網行業的一大特點就是人員流動性高,這使得線上質量經常是“靠天吃飯”。基於黑盒的測試使的項目測試在測試過程中存在以下幾個問題:
(1)黑盒測試受主觀人為因素影響太大:黑盒測試完全依賴測試人員的個人能力,經驗和業務熟悉度,受主觀因素影響太大,不確定性太多,這是產生漏測的根本原因。
(2)測試覆蓋面無客觀數據可衡量:測試對代碼覆蓋程度,質量高低,沒有客觀數據可衡量,完全靠人為主觀介定。雖然我們可以拿到全量或增量代碼覆蓋率數據(增量覆蓋率一般需要運行2次全量用例),但對增量代碼具體路徑是否有覆蓋,只能靠人工分析全量覆率報告,且無法區分哪些是原有代碼,哪些是diff代碼,在代碼量稍微大點的項目中,測試排期基本上不允許這種測試方式,從而把測試人員推向黑盒測試,由於沒有代碼分析支撐,黑盒測試覆蓋率隨着時間和用例增多,便會觸達覆蓋率的天花板,更多的是重復的無效測試。
(3)自動化用例作用無發有效發揮:對於web/api或app 后端服務系統,測試人員對除手工測試外,嘗試最多的測試手段改進就是接口自動化建設,但自動化建設很少有公司在這個方向做的特別好,投放產出比(ROI)特別高的,其根本原因就是自動化的一個核心指標:穩定性太差,隨着項目的迭代,自動化用例積累越來越多,從幾百到幾千,想要這些自動化用例以CI級別觸發(代碼提交一次即觸發一次),用例全部通過穩定在80%以上,幾乎都是不太可能做到的事情。自動化用例穩定性太差,不能產生收益不說,反而會成為QA的包袱,使他們把大量的時間花在自動化用例失敗排查上面,疲於應付,又不能發現有效的bug,久而久之,便對自動化失去信任,甚至廢棄。
☞問題分析與思路☜
筆者所在產品線后端服務是基於java的SSH框架搭建的,模塊數量多, 模塊間基於rpc分步式協議通信,模塊間耦合多, 各個投放系統業務邏輯都比較復雜,且RD和QA新人都比較多,傳統黑盒測試只能通過人員堆砌和不斷的加班加點來適應不斷擴充的業務,這使得項目測試質量很難保持在一個較高水平,和業界面臨問題一樣,也無可避免存在背景中提到的3個問題。產品線的接口自動化測試用例隨着迭代積累,用例數多達幾千個,如果讓這些自動化用例發揮它們的效用呢?
對於背景1,2的問題,我們可以總結為:測試覆蓋面是否可以很容易客觀數據衡量,測試覆蓋面是沒有置信度,且在達到這個置信度的過程中有沒有工具可以支持QA更快更有效達成? 對於背景3中的問題,當自動化用例數到千級別的量級,若100%每次都讓這些用例全部運行通過,幾乎是不可能的事情,那我們能不能減少這些用例數量呢,每次只運行和代碼變更相關的用例,將無關用例的篩選出去呢?
通過對業界和公司其它產品線一些調研,我們發現有些團隊也有在這些問題上做一些探索,即精准測試,但基本上都是聚焦在第3個問題上,即通過用例篩選來減少用例執行以提高升CI的穩定性,思路基本上相同,只是實現過程不各不相同。公司內部一些團隊嘗試也是基於不同的產品特點,如app和前端模板,實現過程不同,這里不再贅述。我們探索方向是,適用於后端服務模塊(web或app后端服務,或api,不局限於實現語言),基於接口自動化的精准測試,並將這個概念做了擴展,不再局限於用例篩選,而是3個層面,即:
(1)自動化用例篩選
(2)測試影響面范圍評估
(3)增量代碼覆蓋率分析
下面具體解釋一下這3個層面的含義。
我們方案/設想:基於自動化用例和覆蓋率信息,獲取單個自動化用例對應代碼覆蓋路徑信息,並建立相應的映射庫(知識庫),做為數據源。如下圖所示
基於獲取的映射庫信息及系統提供的附加能力,支持以下3個基本場景:
(1)自動化用例篩選: 在生成用例和代碼覆蓋路徑映射庫信息后,當RD提測時,可以根據代碼diff計算出變更的方法列表(新增/修改/刪除) ,用方法列表反查映射庫,便可以篩選出與變更代碼相關聯自動化用例,與CI相結合,達到精簡用例,減少執行時間, 同時減少不必要的用例執行,進而提升CI穩定性,減少CI維護排查代價。
(2)測試范圍評估:與場景1相似,在RD 提交代碼代碼后,以變更方法列表做為條件反查映射庫,獲取與之關聯的自動化用例,根據用例URI聚合,並結合用例描述和FE代碼注釋,分析給出手工測試范圍,一是可以減少測試回歸范圍,二是可以防止漏評導致的漏測
(3)增量代碼覆蓋分析:新項目測試過程中,新增自動化用例對增量代碼變更diff 覆蓋信息(生成映射庫過程),可以和增量代碼變更方法列表做為數據源,通過算法生成增量代碼行和分支覆蓋率報告,並具體標記哪個分支或行未覆蓋,QA可以根據增量代碼覆蓋率分析報告,針對性進行用例設計補充,從而提升覆蓋率,減少漏測。同時報告也使得對增量代碼覆蓋情況可量化(常見的增量覆蓋率數據生成要運行2次全量用例集合,自動化穩定性很難保證,手動回歸成本太大,基本不太可行)。另外,對未覆蓋函數或分支進行提醒QA做相應的自動化用例補充,從面形成精准測試,雙向反饋提升良性循環,更有的放矢,更有據可依,更自信任。
☞方案☜
整體的設計方案如下:
方案背景介紹:
(1)接口自動化用例:基於公司通知接口自動化框架平台書寫,分為Http和Rpc兩種接口類型
(2)后端服務實現語言為Java,基於SSH+ RPC分布式協議框架
(3)覆蓋率工具采用Jacoco開源框架
(4)代碼管理系統為公司基於git開發通用代碼管理平台
3.1 基礎用例和覆蓋代碼映射信息庫生成
顧名思義,用例與代碼映射關系即:單個用例與其能覆蓋所有代碼方法列表(不是類,分支或行)映射關系,這里面有2個難點要解決:一是單個用例覆蓋率文件生成代價比較大,二是要消除自動化用例數據構造和清理帶來的代碼覆蓋路徑干擾,在這里我們稱之為消振。
先看第一個問題, 單個用例覆蓋率文件生成代價大。對於收集web或jvm運行后端服務來說,接口級用例覆蓋率收集比單測更為復雜,需要很大的時間、空間開銷,並帶來穩定性隱患。具體如下
(1)時間開銷,每個用例都需要命令行插樁,啟動被測服務,停止補測服務,dump並生成覆蓋率文件,且不說覆蓋率工具的命令行操作,單服務的啟停服務就會帶來不菲的時間開銷
(2)空間開銷調用腳本及源代碼,用列執行,被測服務分別處於不同的機器,在生成覆蓋報告時需要源代碼和覆蓋文件同源,需要額外的操作成本
(3)啟停被測服務給覆蓋文件生成帶來不可控因素,每次服務啟動都可能在啟動中或啟動失敗
常見的離線插樁方式獲取單個用例覆蓋報告流程如下:
通過調研選型,我們發現基於Jacocoon-the-fly模式可以在不停服的情況下,通過api導致覆蓋率文件。關於Jacoco的注入原理以及注入方式,在官方網站上寫的非常詳細了,不做過多贅述。基於 On-the-fly 方式無須入侵應用啟動腳本,只需在 JVM 中通過 -javaagent 參數指定 jar 文件啟動 Instrumentation 的代理程序,代理程序在通過 Class Loader 裝載一個 class 前判斷是否需要注入 class 文件,將統計代碼插入 class ,測試覆蓋率分析就可以在 JVM 執行測試的過程中完成。Jacoco提供了自己的Agent,完成插樁的同時,還提供了豐富的dump輸出機制,如File,Tcp Server,Tcp Client。覆蓋率信息可以通過文件或是Tcp的形式輸出。這樣外部程序可很方便在任意機器上通過api隨時拿到被測程序的覆蓋率。
基於Jacocoon-the-fly 模式獲取單個用例覆蓋率報告流程如下:
再來看第二個問題:如何消除自動化用例數據構造和清理帶來的代碼覆蓋路徑干擾。即單個用例可以獨立重復在不同環境間重復運行,要求用例只能依賴setup/teardown做數據構造和清理,舉例來說,驗證一個update物料屬性A的用例,setup里需要構造2個請求創建物料管理計划,及物料本身,Case為修改物料屬性A接口請求及其對應的校驗點,最后是teardown里的數據清理,刪除物料及基對應的管理計划。在這種情況下,若以Case為粒度收集與之對應的代碼方法列表,會有很多干擾數據進來, 物料所屬的管理計划,物料對應 add和delete接口關聯的方法都會被收集到,所以我們要清除這些干擾數據,要收集以包含校驗點請求與之對應代碼方法列表,即只收集update 物料屬性A這個請求對應關聯方法列表。
來看我們的解決方案:
(1)自動化用例的原子性:單個用例驗證一個接口,且被校驗接口所在請求統一命名,如”request”。
(2)利用AOP原理,在自動化框架的執行器加一個攔截器,在覆蓋率收集開關打開且請求名稱命中request的請求時,請求執行前:reset 被測服務樁數據,請求執行后:用api導出內存中的覆蓋率數據,生成exec文件
(3)在全局變量設置覆蓋率收集開關及其它配置,這樣即不影響其它產品線使用,就可以在同一台機器上完成用例執行,覆蓋率數據收集,樁數據重置,覆蓋率報告生成等一系列操作了。
解決這兩個問題后,用例和覆蓋代碼方法列表映射關系生成方案如下圖:
3.2 自動化用例篩選
有了用例和代碼方法列表映射基礎信息庫后, 我們來看下用例篩選實現邏輯, 這里有2個點,一是如何獲取變更代碼方法列表,二是如何將篩選出散列的用例在自動化框架規則里執行。
先來看獲取變更代碼方法列表,在這里我們沒有采用git原生 diff 函數獲取代碼庫2次代碼提交中間的代碼變更,若基於git原生diff功能,不管是命令行還是api方式,都需要在本地維護一個代碼庫的副本,過於笨重且穩定性差。所以我們在實現是基於公司通用代碼托管平台提供版本對比功能,可以直接獲取2次commitid間的代碼變更文件,並以json格式返回,處理起來更為方便。獲取到代碼變更文件,再基於不同語言方法結構,便可直接獲取到變更方法列表。
用變更方法列表查詢映射信息庫便可篩選出受影響的自動化用例,但這些用例是處在松散狀態,批量執行需要在計划或測試套件綁定后,加上全局變量配置等要素是無法在自動化框架下執行的。為解決這個問題,我們在自動化框架內開發計划動態更新的API,使計划和用例綁定可以動態更新,並同時配置環境,變量,權限等要素,這樣就可以以計划為單位執行篩選出來的自動化用例了。具體的業務邏輯實現見下圖:
3.3 測試影響范圍評估
在敏捷開發模式下, 迭代多,且快。項目在迭代升級的過程,雖然自動化能幫忙覆蓋一部分后端代碼邏輯回歸,但和前端交互部分,當前還沒有好的自動化手段來解決,這部分內容回歸還是主要靠手工測試來保障。影響范圍評估大了,全量手動回歸低效,代價太大;影響范圍評估小了,容易造成漏評漏測。
這種測試影響范圍評估完全靠RB和QA 主觀判斷來評估,強依賴於個人的經驗和能力,再加上人員變更,項目交接等外在因素,經常會有測試范圍漏評的現象。例如:工具類B項目開發過程中,對主流程A底層方法methodA有改動,由於RD和 QA測試范圍評估,經常專注B 升級業務點測試,主流程A的回歸測試沒有評估到,從而導致沒有回歸到造成線上問題。那我們能不能用客觀的數據來精准評估哪部分需要測試,使手工測試范圍也更具體針對性呢?
在這里當某模塊的核心接口主流程場景都被自動化用例覆蓋到以后,我們可以認為,底層業務邏輯的改動方法列表,同樣查詢映射庫關系獲取影響到用例列表,然后將這些用例請求URI或者接口名稱去重,聚合,以報告的形式展示出來, QA便可以根據這個報告更有針對性去做手動回歸測試,防止漏評現象發生,同時可以減少回歸范圍,使回歸更有針對性。具體的實現原理和3.3中用例篩選類似,這里不再展開。
3.4 增量代碼覆蓋率分析
在傳統黑盒測試過程中, 在測試前期能夠比較有效發現bug,但在后期主要依賴個人能力和經驗探索性測試, 往往都是在進行無效的重復測試,而且測試質量沒有置信度,基本上沒有度量,或者因為度量代價太大被裁剪掉了。
就java語言來說,要產出模塊代碼增量覆蓋率,一般要運行前后2個代碼版本全量的用例,分別生成2次生成的全量代碼覆蓋率做,再計算出增量代碼覆蓋率。這種情況下全量手工用例回歸太低效,自動化全量運行情況下,穩定性很難保證,所以沒有可操作性。
另外,在黑盒測試過程,如果想針對提升增量代碼覆蓋率,只能依賴開源工具生成全量代碼覆蓋率報告,但全量代碼覆蓋率報告是無法標記變更代碼和已有代碼區別的,也不具備可操作性。
為解決這2個問題,我們利用從代碼托管平台獲取變更方法列表和新增自動化用例生成的覆蓋率報告,在分析器中組合計算,一次性產出變更代碼增量覆蓋率報告,同時標記出未覆蓋到方法和分支代碼,為測試覆蓋提供衡量數據並可以針對設計用例走到未覆蓋到的代碼,具體的業務邏輯實現如下圖:
到現在我們便可以將精准測試3個落地場景的業務邏輯綜合起來,如下:
☞當前進展(應用現狀)☜
(1)產品線后端模塊接口自動化用例對應增量代碼分支覆蓋率>40%做為測試准出標准,測試覆蓋面可衡量且可針對性提升。
(2)產品線核心模塊local 階段自動化用例回歸,接入自動化用例篩選,CI級別的觸發只運行和代碼變更相關的用例,用例平均精簡比例在50%以上。
(3)產品線線下攔截漏評4次,增量代碼覆蓋率提升過程中發現10個bug。
☞后續規划☜
(1)支持CI trunk/slow 階段接入精准測試
(2)用例篩選能力接入業務端系統級測試流量篩選
(3)通用能力開放和擴展,開放api 並支持常見其它語言和自動化框架用例接入
(4)將更多的測試數據源包含進來,為我所用,如rd的歷史代碼質量情況,方法歷史的線下線上bug情況,都可以囊括進來,為更有效的測試提供底層數據支持,即將更多的測試數據為我所用
(5)擴展精准測試范圍到精細化測試,針對於代碼變更,甚至不同的RD,需要進行什么范圍的測試,要進行哪些類型的測試,這些測試有什么的技術手段,測試到什么樣的程度,系統都可以給出分析建議和衡量指標。