“程式碼變更覆蓋率”在後端測試中的實踐
本文來自網易雲社群
作者:倪志風
最近一直對測試覆蓋率方面的內容比較感興趣,雖然很多專案都早已經用上了Jacoco來實現測試覆蓋率的統計,但是很少看到實際專案中基於覆蓋率統計來指導測試的實踐。這篇文章是我近期基於程式碼變更風險(CR)平臺 (http://cr.qa.netease.com/)對猛獁大資料系統的後臺排程元件Azakban的一個小版本的測試實踐。
個人認為,程式碼覆蓋率應該是對一個版本測試情況的一個重要考量。不能說程式碼覆蓋率高,就是沒有風險。但是,相反,我覺得如果程式碼覆蓋率很低,那就是客觀地存在較大風險,說明我們測試的充分度不夠。CR平臺,其實是基於jacoco覆蓋率統計的結果之上,將提測版本與基準版本(通常是上一個線上穩定版本)的程式碼庫進行比對,統計出一個版本提測後開發變更程式碼的覆蓋情況。另外,藉助了其他開源工具(ckjm、javancss等)展示了程式碼中類之間的相互呼叫關係以及複雜度資訊等等。
下面是我在猛獁4.8.5版本中,藉助CR平臺對Azkaban相關需求的測試實踐:
例子一:
1. 看需求和JIRA
開發寫的需求:
“ 補資料優化,增強順序性,優化例項生成數量:原來有的邏輯是Trigger每個一分鐘觸發一次,然後產生一個補資料例項到分發佇列,分發佇列每次分發前,先檢查有沒有正在執行的例項,如果沒有就隨機選擇一個例項分發下去執行。如果有,重新放回佇列。這個邏輯存在的問題是,會產生大量的例項在分發佇列,耗費記憶體和CPU時間。現在做的優化是:Trigger每次觸發前,先檢查分發佇列以及已經分發下去的例項裡邊是否有相關的補資料例項。如果有,則跳過這次觸發。另外可以通過 az.backfill.concurrent.num 指定補資料例項產生的併發度(範圍1~100,缺失預設值 1)。”
看著可能有點暈,我稍微將它簡化一下就是:
a. 補資料排程,原本可能會產生大量併發執行的例項,現在限制併發例項數只能是1。即:前一次例項執行完成後,後一次排程例項才會生成。
b. 提供引數, az.backfill.concurrent.num來控制允許的併發例項數量,該引數的範圍是1-100。
至於JIRA,我這邊就不重複添圖了,大致跟需求內容差不多。
2. 檢視Gitlab:
開發提交gitlab程式碼時與jira做了關聯,點選連結可以直接跳轉到gitlab:
可以看到該jira影響的類有兩個,分別是:GlobleAttribute.java (只添加了一行常量定義)和 TriggerManager.java
3. 檢視CR:
開始測試該功能前,先看一眼CR的覆蓋情況(CR之前執行過自動化測試用例和其他手工測試用例):
可以看到,在表格裡沒有出現GlobleAttribute.java(不知是否是因為該類只添加了一行常量定義,改動太小的緣故),而TriggerManager.java類的改動部分覆蓋率為63.6%。點進去再仔細看下新修改的行的覆蓋情況:
可以看到“新修改”的未覆蓋部分(紅色)主要是因為 if (t.getContext().containsKey(GlobleAttribute.AZ_BACKFILL_INSTANCES_NUM))判斷沒有命中,因為該需求引入了新的引數 az.backfill.concurrent.num 指定補資料例項產生的併發度 。而該引數我們還並沒有設定過。
此外,下面的未覆蓋部分, if 判斷的後半段 flow.getScheduleType().equals(ScheduleType.BACKFILL)未命中的原因是因為我們還並沒有執行過補資料的排程。
4. 執行手工測試
對一個作業流設定補資料排程100次,指定補資料例項產生的併發度為5(設定引數az.backfill.concurrent.num 的值為5),驗證補資料排程按照既定場景正確執行。
5. 測試結果
查資料庫顯示,補資料排程按照順序執行, 但是例項個數始終為1。
顯然,該需求的基本功能生效了(從以前的併發例項數不受限,現在例項個數為1),但是併發度引數並沒有生效。
6. 結果分析排查
重新執行覆蓋率統計,再來看一眼CR平臺的新的結果:
可以看到,覆蓋率有一定的提升。但是為什麼併發引數設定沒有生效呢? 來看一眼伺服器上的執行日誌,拉到日誌的最後,看到有一條頻繁列印的日誌:“backfill trigger 3959, .......”:
再對照一下CR平臺上變更的程式碼:
可以看到,下面這部分“新修改”的程式碼應該列印的日誌還處於“未覆蓋”的狀態。往上看,發現紅框中圈出的程式碼段裡列印的日誌 剛好就是伺服器上後臺的日誌。看一眼判斷條件: “判斷該作業流是否已經有執行例項正在執行中,且該正在執行中的作業流例項的開始時間距離現在不到10分鐘 ” 。
這樣就初步解釋了 目前階段為什麼我們沒看到“新修改”日誌列印,以及例項沒有生成的直接原因。即: 被之前的判斷條件錯誤攔截了。
等待10分鐘,我們繼續檢視資料庫的結果,發現併發的例項數仍為1。 再看一眼CR的覆蓋情況:
可以看到,剛才未覆蓋的這段日誌列印,在10分鐘過去後被執行了。為了確認該資料的準確性,再看一眼日誌,這次我們直接搜尋“Reach az.backfill.instances.num ......”這條日誌:
可以看到,日誌確實出現了變化。也就是說,伺服器執行的程式碼進入了判斷: “到達了補資料例項個數的上限 isReachBackfillInstanceNum(t)” 。
然而,我們設定的上限明明是5,為什麼卻顯示已經達到上限,而資料庫裡的實際併發數還是1呢?
再看一眼,變更程式碼中未覆蓋的變更行:
可以發現,在isReachBackfillInstanceNum(t)方法中,剛好找到了我們想要的內容:預設的補資料例項個數backfillInstancesNum為1,當排程t的上下文資訊context中包含引數GlobalAttribute.AZ_BACKFILL_INSTANCES_NUM時,就用該對應的值賦值給backfillInstancesNum。且會對該範圍進行1-100的判斷。
而這段程式碼正好沒有被執行。
再次進入原始碼檢視,發現了個驚人的事情:
這個AZ_BACKFILL_INSTANCES_NUM引數的實際key值,應該是az.backfill. instances.num,而開發兄弟在需求和jira中寫的都是“az.backfill. concurrent.num”。
此時,我的心情是複雜的。此處需要省略1000字。
如你所知,後續修改引數key後,重新測試,成功測試通過。通過後的覆蓋率統計情況:
唯一沒有覆蓋的行,是因為設定的併發引數的值是5,在該1-100範圍內,所以沒有進入該判斷。
例子二:
篇幅有限,再來簡單介紹另外個利用CR平臺完善測試的例子。
需求:
將執行節點的流例項併發數量的值寫進db,在節點服務啟動時,優先從db中讀取載入進記憶體。如果db中不包含,再從配置檔案讀取。
簡單測試後, 看下CR平臺的覆蓋情況:
可以看到,對於基本的正常功能,我們還是覆蓋到了。 但是仍有大部分語句沒有覆蓋,而這部分未覆蓋的內容其實就是查詢資料庫時發生異常情況。
知道未覆蓋的原因,就好辦了,我們只要製造資料庫查詢異常就可以模擬該場景。
測試方式:
執行資料庫語句alter table executors drop column threads_num; 將mysql中executor表的threads_num欄位給刪掉。然後重新啟動executor。並確保executor可以正常工作。
再次執行覆蓋率統計,檢視CR平臺的結果:
可以看到,這下“新修改”的程式碼全部覆蓋了。可以妥妥地在jira上標上這功能測試通過。
總結
最近看了一些業界的測試方法,發現基於覆蓋率統計的白盒測試的熱度正在逐漸提升,包括騰訊、去哪兒等一些公司都有基於覆蓋率統計指導測試的不少實踐。包括分析程式碼增量覆蓋率,分析每條用例對覆蓋率的貢獻來精簡測試用例集、剔除無效用例,基於執行覆蓋統計將每條用例與程式碼模組相對應等等。所幸,我們也有CR平臺,基於CR平臺,我們後續可以做的事情應該還有很多。
網易雲 ofollow,noindex" target="_blank">免費體驗館 ,0成本體驗20+款雲產品!
更多網易研發、產品、運營經驗分享請訪問 網易雲社群 。
相關文章:
【推薦】 從golang的垃圾回收說起(上篇)
【推薦】 Omad群組部署、依賴部署一鍵解決