天天看點

精準測試體系建構

作者:閃念基因

1. 功能測試的痛點

1.1 測試範圍和效果

版本提測後,開發往往會說,影響範圍比較大,做個主鍊路或者全量回歸吧,我隻改了幾行代碼,為什麼要回歸這麼長時間?等等。

測試完成後,測試往往會說,測試保證測試用例全部執行到位,考慮不到的沒辦法。

代碼改動後的 影響範圍評估,以及測試完成後的 覆寫面評估 是個難題,目前大部分是依靠個人經驗和業務熟悉度判斷大概的範圍。

這種評估方式以主觀判斷為主,缺乏資料支撐,希望能有系統工具提供客觀資料,基于資料進行量化評估。

1.2 測試成本和效率

産品疊代越來越多,代碼改動會對産品已有功能産生影響,除了依賴 CI 的自動化測試,人工回歸也必不可少,其成本和效率需要考慮。

為了保證品質,完美的回歸測試是全流程覆寫,但是改動五分鐘回歸兩小時,測試成本巨大,還會有很多無效測試。

理想的回歸測試是覆寫修改的内容,用有限的操作發現全部的問題。

如果能建立 代碼與用例的映射關系, 當代碼發生改動時推薦出關聯的用例,就能讓測試更 精準地回歸,降低成本,提高效率。

1.3 測試和開發協同

  • 目前 Bug 處理的流程是:測試執行測試用例,發現 Bug 送出禅道,禅道提醒開發,開發了解重制 Bug ,遠端調試解決。

整個流程中,基于測試同學送出的 Bug 描述,開發了解,重制,修複 Bug 可能需要花費大量的時間。

如果測試通過執行用例就可以找到出 Bug 的代碼回報給開發,開發修複 Bug 會快很多,開發和測試之間的協同工作就會輕松很多。

  • 測試需要開發協助分析未覆寫代碼來補充測試用例;開發需要代碼覆寫情況來優化代碼(去掉無用代碼等)

目前大部分測試在拿到覆寫率報告後,對報告中染紅色的代碼,由于不熟悉代碼,需要去問開發,進行用例補充。

如果能在覆寫率報告中增加批注功能,開發通過批注方式告訴測試這段代碼需要補充什麼業務場景用例,這樣就能提高效率。

2. 整體解決方案

2.1 架構

精準測試體系建構

2.2 UML時序圖

精準測試體系建構

2.3 雙向追溯之正向追溯

**正向追溯 **的核心是将測試用例和代碼關聯起來,建立用例代碼庫,這是推薦回歸用例的基礎,是重中之重。

将測試用例和代碼關聯起來的核心是 動态調用鍊,要擷取動态調用鍊就需要 Agent 注入應用,采集應用運作時資料。

公司有基于 OpenTelemetry 的可觀測平台,能提供動态調用鍊路,為我們建構用例代碼庫提供了基礎能力。

精準測試體系建構

2.3.1 建構用例代碼庫

有了生成動态調用鍊路的能力,接下來就可以建構 用例代碼庫 了,用例代碼庫的建構可以采用三種方式。

  • 測試同學手工執行用例,一條用例對應多個請求,一個請求一條調用鍊路。
  • 跑自動化腳本,一個腳本對應多個請求,一個請求一條調用鍊路。
  • 流量錄制和回放,直接就是一個請求一條調用鍊路

将用例和其調用鍊路以快照的形式存儲到ES或者對象存儲,為後續的用例推薦提供基礎庫,這裡唯一比較麻煩的就是用例代碼庫的維護。

每次版本的新功能由測試重點測試,測試過程中将其用例與代碼關聯,作為後續用例推薦的基礎。

老功能由用例代碼庫推薦出用例進行自動化或手工測試或者流量會方法,是以,對用例代碼庫的維護就尤其重要。

可以為每個快照設定周期,過了這個周期推薦出的用例就要進行人工幹預,去分析是否是可用的用例。

關于用例代碼庫的建構目前還在設計中......

2.3.2 測試用例推薦

建構了用例代碼庫後,接着就需要進行 測試用例推薦。

利用現有能力 clone compile 提測分支和 master 代碼,進行差異代碼擷取,再根據差異代碼反查調用鍊路,計算出需要測試的範圍。

到這裡,雖然能推薦出測試範圍,但是可能在準确性方面不盡如人意。

比如,當底層或公共代碼發生改動時,由于這些代碼關聯的用例較多,系統會推薦出大量備援用例,影響測試效率。

那如何提升推薦的精準度?以方法的級别進行推薦,會産生備援用例,影響推薦精準度,具體如下:

public static void fun(int a){
  if(a == 0){
       System.out.println("走了 a=0 的分支");
    }else if(a == 1){
       System.out.println("走了 a=1 的分支");
    }else{
       System.out.println("走了 非a=0且非a=1 的分支");
    }
}
           

fun 方法假設用3條用例進行覆寫:

  • 用例 1:傳入 a = 0
  • 用例 2:傳入 a = 1
  • 用例 3:傳入 a = x

當分支 a = 0 内的代碼發生變化,如果精确到方法級别的推薦政策進行推薦,這三個用例都會被推薦出來。

但實際上用例 2 和用例 3 是完全不會受到影響的,這樣就産生了備援用例。

是以,在建立用例代碼庫的時候,就要将用例與代碼分支進行關聯,

在進行分支解析時,得到用例執行的分支條件及分支所對應的代碼塊,推薦時差異代碼的計算也要精确到有哪些分支發生變更。

是以,首先将本次送出代碼的分支條件與用例庫中用例的分支條件進行比對,比對一緻再對比分支内容有無變化。

如果發生變化,則需要做推薦,如果沒有發生變化,就說明它不受影響,也無需推薦。

2.4 雙向追溯之逆向追溯

研發流程中,開發從 master 拉取分支 feature_xxx 進行開發,開發完成後進行自測,冒煙,再送出測試。

測試的内容包含新的需求,以及開發測試根據個人能力、經驗、業務熟悉程度進行的影響範圍評估。

這些評估方式不客觀,無資料支撐,容易産生遺漏或者擴大測試範圍。

2.4.1 目标

根據開發代碼的改動,包括 SQL 的改動,計算出所有受影響的 Dubbo 接口和 Rest 接口,進而找出關聯的功能用例。

2.4.2 實作流程

精準測試體系建構

首先,開發 MR 代碼後,精準測試服務 clone master 分支代碼,并 clone 和 compile 提測分支代碼,為下一步的擷取代碼差異做準備。

其次,擷取master 分支代碼和提測分支代碼 的差異,除了 JAVA 代碼的變更,還考慮了 SQL 的變更,這些變更也會影響到業務。

然後,通過 ASM 解析提測分支代碼的 class 位元組碼,生成每個類的靜态方法調用鍊,ASM 為我們提供了友善的處理位元組碼的能力。

通路 class 檔案中的類,需要重寫 ASM 的 ClassVisitor 的 visit() 方法。

通路 class 檔案中的方法,需要重寫 ASM 的 ClassVisitor 的 visitMethod() 方法進行方法調用關系解析。

通路 class 檔案中的注解,需要重寫 ASM 的 ClassVisitor 的 visitAnnotation() 方法。

相對于 Rest 接口,要拿到請求路徑,請求路徑是寫在類+方法注解裡的,是以,需要解析類注解擷取請求路徑,

比如 Spring 中的 @RestController 和 @Controller 等注解。

相對于 Dubbo 接口,要知道 Dubbo 的服務提供者和服務消費者,同樣需要解析注解,比如 @DubboService 和 @Service 。

關于識别 Dubbo 接口需要注意兩點:

  1. @Service 注解需要相容 Dubbo 版本 apache(3.x) 和 alibaba(2.x)。
  2. 相容 Dubbo 的 XML 配置方式。

最後,通過差異代碼查詢靜态方法調用鍊,得到受影響的調用鍊,再追溯到調用鍊的入口,即 Dubbo 接口和 Rest 接口。

2.4.3 平台互動

測試時隻需要填寫在版本測試過程中涉及到的應用的 Git 位址和提測分支,點個按鈕等幾分鐘就可以擷取到結果。

第一步:新增 Git 基礎資訊并點選執行

精準測試體系建構

第二步:檢視結果

精準測試體系建構

目前我們做到了擷取受影響的 Dubbo 接口和 Rest 接口,通過什麼政策建立接口和測試用例的關聯關系還在考慮。

我們希望能做到計算出來的 Dubbo 接口和 Rest 接口直接生成回歸用例集,直接執行接口自動化,測試主要精力放在新的功能測試上。

通過靜态代碼分析出的調用鍊有幾點需要說明:

  1. 由于分析的是靜态代碼,是以如多态、動态代理、aop 這些需要代碼運作時的調用鍊目前無法識别,需要結合動态調用鍊進行分析。
  2. 分析應用的靜态代碼得到的方法調用鍊類似靜态測試,不夠精準,同樣需要結合動态調用鍊,采取動靜結合的方式進行精簡。
  3. 靜态分析是分析單個應用,暫時不涉及到跨應用調用,跨應用同樣需要結合動态調用鍊進行整個鍊路的分析。

3 覆寫率

測試過程中通過使用 Jacoco 代碼覆寫率統計測試覆寫了多少代碼。注意,是統計測試了多少代碼,無法證明測試結果的有效性。

如果代碼本身就存在錯誤,Jacoco 本身是發現不了的,需要測試根據業務場景去驗證業務邏輯。

不過,Jacoco 能告訴我們測了多少代碼,有哪些沒測到的進行分析是否要進行補充測試用例。

微服務架構下,同一個應用會部署在不同的節點,請求根據一定的政策打到不同的節點,收集覆寫率資料就需要考慮到所有節點。

整個研發階段通過不同的測試類型來保障産品品質,為了保證收集到的覆寫率資料盡可能全,不同研發階段都需要收集覆寫率資料。

原生 Jacoco 隻支援相同代碼的覆寫率資料合并,不支援代碼發生變動的覆寫率資料合并。

就是說如果在測試過程中,開發修改了代碼,那麼修改前後收集到的兩次覆寫率資料是合并不起來的。

合并的結果永遠是最近一次收集到的覆寫率資料,修改代碼之前的覆寫率資料會被丢棄。

綜上所述,相信測試同學能體會到目前靈活模式下覆寫率資料收集不全的痛點:

  • 同一個應用多節點 (多執行個體) 的覆寫率資料如何合并。
  • 不同研發階段 (單元,冒煙,系統,回歸) 的覆寫率資料如何合并。
  • 開發每次改動後送出代碼,多次部署的覆寫率資料如何合并。

3.1 覆寫率統計

結合公司内部的各種平台,我們做了覆寫率平台,用于測試過程中進行覆寫率資料的收集和報告的擷取,具體流程如下:

首先,clone&compile master 和提測分支,master 分支代碼一旦下載下傳,整個版本周期就固定住,這樣避免開發合代碼導緻資料錯亂。

然後,CI/CD 平台 dump 覆寫率資料,由于應用會部署多個執行個體,我們也支援了多執行個體覆寫率資料的合并生成最終的覆寫率檔案。

接着,因為我們做的是增量代碼覆寫(當然我們也支援了全量),是以将 master 分支和提測分支取差異代碼

最後,利用 src 源碼,class 位元組碼,exec 檔案生成報告。

精準測試體系建構

這裡最重要的點是,如果代碼發生變更,我們會把開發每次 commit 代碼 clone&compile 并 dump其覆寫率資料進行合并,

最終用最新的 commit 代碼和 master 進行增量覆寫率統計。

3.2 覆寫率報告

為了降低測試使用覆寫率平台的門檻,我們盡量做到了在不增加測試工作量前提下快速完成代碼覆寫率統計,并拿到覆寫率報告。

在開發第一版的時候由于沒有跟 CI/CD 平台打通,徒增了測試同學的工作量。

收集覆寫率資訊所需要的代碼 Git 資訊,分支資訊,commit 資訊需要測試同學手動填寫,在使用體驗上不太友好。

是以我們梳理了整個流程,對整個流程進行優化,最終優化到測試同學隻需要兩步即可完成代碼覆寫率統計:

3.2.1 注入 Agent

在 CI/CD 平台支援下做到一鍵注入 Agent,我們隻需把 Agent jar 和參數給到 CI/CD 平台 ,平台幫我們對 Agent 建構鏡像,完成注入。

3.2.2 生成報告

原生的 Jacoco 在生成報告之前要分别進行 dump 和 merge 操作,我們把這些步驟都做在了一起,

隻需從 CI/CD 平台拿到 Git 資訊,分支資訊,commit 資訊,然後先執行 dump,再 merge ,最後 report,做到一鍵擷取覆寫率報告。

當然,這裡還有優化的空間,比如目前我們在統計覆寫率資料的過程中需要源碼和編譯後的位元組碼,

源碼我們是直接 clone 或者 pull 的,編譯是拿到源碼後進行本地編譯的,

這裡潛在的問題是編譯特别耗時耗資源,經過測試,基本上一台 4C8G 的機器,同時有 4 個應用編譯時 CPU 使用率會飙升到95%,

針對這個問題我們的辦法是直接用 CI/CD 平台已經編譯好的 jar,拿過來直接解壓成位元組碼檔案。

下面就是覆寫率平台,測試在 CI/CD 平台注入 Agent 後,隻需要填寫幾個字段 (重點就是應用名和治理環境) 就可以收集覆寫率報告了。

精準測試體系建構

3.3 報告優化

Jacoco 原生的報告在可讀性方面不太友好,測試同學實際上隻想知道哪些代碼覆寫了,哪些代碼未覆寫,

對圈複雜度和指令覆寫度的名額不是太了解,也不太特别關注。是以我們對報告進行了如下優化:

  • 删除圈複雜度 Cxty 及指令 Instructions 的名額項
  • 将 miss% 修改為 covered%,并中文展示覆寫情況
  • 用 3D 環餅圖展示各個名額的具體覆寫情況

效果如下:

精準測試體系建構

後續還會繼續對報告進行優化,友善測試和開發解讀,包括如下:

  • 在報告源碼頁面進行辨別,哪些代碼是新增的,哪些代碼是修改了的。
  • 在報告源碼頁面增加測試和開發協同工作的機制,友善進行業務代碼解讀和用例補充。

4.總結與規劃

目前為止,我們做了逆向追溯即測試範圍的評估,用來解決 測什麼 的問題,還做了增量代碼覆寫率用來解決 測的怎麼樣 的問題。

未來,一方面會持續疊代優化現有的能力,還會進一步完善整個精準測試體系,包括如下:

  • 完善精準測試體系中的正向追溯,補齊能力,進一步解決 測什麼,最大限度地輔助測試同學高效完成測試。
  • 把這些能力嵌入到研發流程中,盡量做到測試過程中無感覺完成精準測試。

作者:南易

來源:微信公衆号:政采雲技術

出處:https://mp.weixin.qq.com/s/O-dCNGIhfnp6weDwidbeFg