JavaScript覆蓋率統計實現
主要需求
1、 支援browser & nodejs
因為javascript既可以在瀏覽器環境執行,也可以在nodejs環境執行,因此需要能夠統計兩種環境下單元測試的覆蓋率情況。
2、 透明、無縫
使用者寫單元測試用例的時候,不需要為了支援覆蓋率統計多寫程式碼,之前寫的用例無需修改就可以直接統計覆蓋率情況。
原理
javascript覆蓋率的相關文章比較少,下面的圖是通過閱讀開源javascript覆蓋率工具istanbul及開源測試框架Karma的覆蓋率外掛karma-coverage得出的。javascript覆蓋率統計的核心思想是,在原始碼相應的位置注入統計程式碼,當程式碼執行之後,根據統計程式碼統計的資料確定程式執行的路徑,最終生成覆蓋率統計報告。
1. 轉換(instrument)
- 使用開源工具Esprima對原始碼進行語法分析生成語法樹
- 在語法樹相應的位置注入統計程式碼,在程式執行到這個位置的時候對相應的全域性變數賦值,確保執行之後能夠根據全域性變數知道程式碼的執行流程
- 使用開源工具Escodegen根據注入之後的語法樹生成對應的javascript程式碼,即轉換之後的程式碼(instrumented code)
注:這裡進行語法分析的好處是,針對書寫不規範的程式碼(比如一行多個語句),依然能夠很好統計出分支覆蓋和組合覆蓋等資訊。
2. 執行(run)
這一步需要先載入轉換後的程式碼:
- nodejs:直接通過對
require
語句進行hook來無縫實現,後面會詳細介紹 - 瀏覽器環境:需要將轉換後的程式碼傳給瀏覽器。如果是karma之類的帶server的測試框架,需要通過socket傳輸至瀏覽量器,執行完之後再將包含覆蓋率資訊的執行結果傳回server,生成測試報告
然後執行單元測試,產生的統計資訊會掛在全域性變數this
下面。對於瀏覽器環境,this
就是window
,而對於nodejs環境this
就是global
。
3. 生成報告(report)
這一步會根據全域性標量中的覆蓋率資訊生成特定格式的報告,如html、lcov、cobertura、teamcity等。
一個例子
//source code
function abs(num){
if(abs > 0)
return num;
else
return -num;
}
//instrumented code
var __cov_iypKC$dWI6uJFmvxThycaA = (Function(`return this`))();
if (!__cov_iypKC$dWI6uJFmvxThycaA.__coverage__) { __cov_iypKC$dWI6uJFmvxThycaA.__coverage__ = {}; }
__cov_iypKC$dWI6uJFmvxThycaA = __cov_iypKC$dWI6uJFmvxThycaA.__coverage__;
if (!(__cov_iypKC$dWI6uJFmvxThycaA[`/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js`])) {
__cov_iypKC$dWI6uJFmvxThycaA[`/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js`] = {"path":"/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js","s":{"1":1,"2":0,"3":0,"4":0},"b":{"1":[0,0]},"f":{"1":0},"fnMap":{"1":{"name":"abs","line":1,"loc":{"start":{"line":1,"column":-15},"end":{"line":1,"column":17}}}},"statementMap":{"1":{"start":{"line":1,"column":-15},"end":{"line":6,"column":1}},"2":{"start":{"line":2,"column":1},"end":{"line":5,"column":14}},"3":{"start":{"line":3,"column":2},"end":{"line":3,"column":13}},"4":{"start":{"line":5,"column":2},"end":{"line":5,"column":14}}},"branchMap":{"1":{"line":2,"type":"if","locations":[{"start":{"line":2,"column":1},"end":{"line":2,"column":1}},{"start":{"line":2,"column":1},"end":{"line":2,"column":1}}]}}};
}
__cov_iypKC$dWI6uJFmvxThycaA = __cov_iypKC$dWI6uJFmvxThycaA[`/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js`];
function abs(num){__cov_iypKC$dWI6uJFmvxThycaA.f[`1`]++;__cov_iypKC$dWI6uJFmvxThycaA.s[`2`]++;if(abs>0){__cov_iypKC$dWI6uJFmvxThycaA.b[`1`][0]++;__cov_iypKC$dWI6uJFmvxThycaA.s[`3`]++;return num;}else{__cov_iypKC$dWI6uJFmvxThycaA.b[`1`][1]++;__cov_iypKC$dWI6uJFmvxThycaA.s[`4`]++;return-num;}}
node.js整合覆蓋率
通過hook可以直接無縫的載入轉換後的程式碼,可以對下面兩種語句進行hook:
require
vm.createScript
對require進行hook的程式碼是通過對Module._extensions[`.js`]
進行賦值實現的:
function hookRequire(matcher, transformer, options) {
options = options || {};
var fn = transformFn(matcher, transformer, options.verbose),
postLoadHook = options.postLoadHook &&
typeof options.postLoadHook === `function` ? options.postLoadHook : null;
Module._extensions[`.js`] = function (module, filename) {
var ret = fn(fs.readFileSync(filename, `utf8`), filename);
if (ret.changed) {//載入instrument之後的程式碼並執行
module._compile(ret.code, filename);
} else {//載入原來的程式碼並執行
originalLoader(module, filename);
}
if (postLoadHook) {
postLoadHook(filename);
}
};
}
hook使覆蓋率的整合變得簡單,甚至不需要寫程式碼,比如Mocha的覆蓋率整合,只需要改用如下的呼叫方式即可:
istanbul cover _mocha -- -R spec test/spec
瀏覽器整合覆蓋率
瀏覽器整合覆蓋率就稍微麻煩一點,好在istanbul提供了API:
1. 轉換程式碼(呼叫istanbul的Instrumenter介面)
2. 將instrumented code傳送到瀏覽器(*自己實現*)
3. 將包含覆蓋率資訊的執行結果發回server(*自己實現*)
4. 根據返回的覆蓋率資訊生成覆蓋率報告(呼叫istanbul的Reporter介面)
相關文章
- 【Lua】實現程式碼執行覆蓋率統計工具
- 簡記前後端如何實現統計測試覆蓋率後端
- iOS 覆蓋率檢測原理與增量程式碼測試覆蓋率工具實現iOS
- PouchContainer 整合測試覆蓋率統計AI
- 前端精準測試探索:覆蓋率實時統計工具前端
- 程式碼覆蓋率與測試覆蓋率比較
- EMMA 覆蓋率工具
- python自動統計zabbix系統監控覆蓋率Python
- Google開源ScriptCover,JavaScript程式碼覆蓋率工具GoJavaScript
- 程式碼覆蓋率測試:從誤傳到現實
- php實現矩形覆蓋PHP
- pHp程式碼覆蓋率PHP
- 程式碼覆蓋率分析
- JaCoCo計算程式碼覆蓋率原理
- 單元測試的覆蓋率計算
- 基於JaCoCo的Android測試覆蓋率統計(二)Android
- 關於super-jacoco測試覆蓋率具體實現
- 測試開發之單元測試-實現Git增量程式碼的Jacoco覆蓋率統計Git
- idea2022.1 檢視單測覆蓋率展示分支覆蓋率Idea
- 基於Jacoco的單元測試程式碼覆蓋率統計
- go 程式碼覆蓋率測試Go
- 程式碼測試覆蓋率分析
- java覆蓋率檢測-jacocoJava
- 什麼是程式碼覆蓋率
- vivo 基於 JaCoCo 的測試覆蓋率設計與實踐
- 微軟Windows 10創意者更新秋季版覆蓋率覆蓋率再達新高:已達85%微軟Windows
- 進化的覆蓋率-程式碼實時染色系統 - 周為 / 翟帥
- 如何實現室內無線覆蓋
- ReactNative 多端程式碼覆蓋率調研及實踐React
- 聊聊Go程式碼覆蓋率技術與最佳實踐Go
- 如何提高全城wifi訊號覆蓋率WiFi
- Jacoco--測試覆蓋率工具
- 多程式下的測試覆蓋率
- Mockito提升單元測試覆蓋率Mockito
- 模型評估過程中:命中率/覆蓋率模型
- 在做服務端程式碼覆蓋率或者準備做程式碼覆蓋率的兄弟們,來聊聊???服務端
- 如何使用 jacoco 統計多個 docker 容器服務的測試覆蓋率Docker
- Jenkins實現iOS自動化測試及覆蓋率報告輸出JenkinsiOS