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;elsereturn -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接口)

轉載于:https://www.cnblogs.com/jzdwajue/p/6900462.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/392665.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/392665.shtml
英文地址,請注明出處:http://en.pswp.cn/news/392665.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode 328. 奇偶鏈表(雙指針)

給定一個單鏈表,把所有的奇數節點和偶數節點分別排在一起。請注意,這里的奇數節點和偶數節點指的是節點編號的奇偶性,而不是節點的值的奇偶性。 請嘗試使用原地算法完成。你的算法的空間復雜度應為 O(1),時間復雜度應為 O(nodes)…

NSLog打印當前文件,當前函數,當前行數

NSLog(”%s, %s, %d”, __FILE__, __FUNCTION__, __LINE__); 轉載于:https://www.cnblogs.com/shenfei2031/archive/2011/08/06/2129636.html

單元格內容分列多行_姓名太多,放在一列打印時浪費紙張,可以分成多行多列打印...

在日常工作中,往往會碰到這種情況(如下圖):只有一列數據,而且比較多,如果打印起來就浪費紙張,然后復制、粘貼把表格變成幾列,方便打印。今天小編和大家分享不用復制、粘貼,就能快速完成一列分成…

caesar加密_如何編寫Caesar密碼:基本加密簡介

caesar加密by Brendan Massey由布倫丹梅西(Brendan Massey) The Caesar Cipher is a famous implementation of early day encryption. It would take a sentence and reorganize it based on a key that is enacted upon the alphabet. Take, for example, a key of 3 and th…

Java中接口、抽象類與內部類學習

2019獨角獸企業重金招聘Python工程師標準>>> Java中接口、抽象類與內部類學習 接口與內部類為我們提供了一種將接口與實現分離的更加結構化的方法。 抽象類和抽象方法 抽象方法:僅有聲明而沒有方法體。 抽象類:包含一個或多個抽象方法的類&am…

leetcode 402. 移掉K位數字(貪心算法)

給定一個以字符串表示的非負整數 num,移除這個數中的 k 位數字,使得剩下的數字最小。 注意: num 的長度小于 10002 且 ≥ k。 num 不會包含任何前導零。 示例 1 : 輸入: num “1432219”, k 3 輸出: “1219” 解釋: 移除掉三個數字 4, 3, 和 2 形成…

javascript 自定義Map

遷移時間:2017年5月25日08:24:19 Author:Marydon 三、自定義Map數據格式 需特別注意的是: js中沒有像java中的Map數據格式,js自帶的map()方法用于:返回一個由原數組中的每個元素調用一個指定方法后的返回值組成的新數組。 map()使…

gif分解合成_如何通過分解和合成使復雜的問題更容易

gif分解合成Discover Functional JavaScript was named one of the best new Functional Programming books by BookAuthority!“發現功能JavaScript”被BookAuthority評為最佳新功能編程書籍之一 ! Our natural way of dealing with complexity is to break it in…

vs2005 新建項目一片空白

最近在研究 workflow fundation ,但是在安裝了他的extensions之后,發現VS2005 新建項目一片空白,除開workflow其他的項目模板全部丟失,新建項目對話框中空空如也。查閱資料后發現,可以通過 命令 devenv.exe /InstallVSTemplates 來…

docker導入鏡像 liunx_docker掃盲?面試連這都不會就等著掛吧

推薦閱讀:java喵:6大面試技能樹:JAVA基礎JVM算法數據庫計算機網絡操作系統?zhuanlan.zhihu.com一只Tom貓:都是“Redis惹的禍”,害我差點掛在美團三面,真是“虛驚一場”!?zhuanlan.zhihu.com現…

crontab里shell腳本將top信息寫入文件

crontab里shell腳本將top信息寫入文件: 注: 1、top -n 1代表執行1次退出(默認top是不退出的),-d 1代表每1秒執行1次 2、crontab里需加/bin/bash # crontab -e */5 * * * * /bin/bash /usr/local/bin/top.sh # vi top.sh #!/bin/ba…

leetcode 1030. 距離順序排列矩陣單元格(bfs)

給出 R 行 C 列的矩陣&#xff0c;其中的單元格的整數坐標為 (r, c)&#xff0c;滿足 0 < r < R 且 0 < c < C。 另外&#xff0c;我們在該矩陣中給出了一個坐標為 (r0, c0) 的單元格。 返回矩陣中的所有單元格的坐標&#xff0c;并按到 (r0, c0) 的距離從最小到…

Linux iptables:規則原理和基礎

什么是iptables&#xff1f; iptables是Linux下功能強大的應用層防火墻工具&#xff0c;但了解其規則原理和基礎后&#xff0c;配置起來也非常簡單。 什么是Netfilter&#xff1f; 說到iptables必然提到Netfilter&#xff0c;iptables是應用層的&#xff0c;其實質是一個定義規…

太陽系八大行星碰撞的視頻_火星的身世:從太陽系的起源說起

大約46億年前盤狀的太陽星云從一大片又冷又暗的氣體云中誕生太陽自己并沒有任何暴露確切年齡的線索&#xff0c;我們之所以能夠知道太陽系的“生日”&#xff0c;是因為迄今從隕石中找到的最古老固體物質&#xff0c;年齡約為45.68億年。一般認為&#xff0c;太陽系的各個地方是…

refract推導_我們如何利用Refract來利用React式編程的力量

refract推導by Joe McGrath通過喬麥克格拉斯 我們如何利用Refract來利用React式編程的力量 (How we harnessed the power of reactive programming with Refract) Have you ever wondered how open-source libraries built by companies come into existence?您是否想過公司建…

sql server:查詢系統表

---查看所有存儲過程或視圖的位置 select a.name,a.[type],b.[definition] from sys.all_objects a,sys.sql_modules b where a.is_ms_shipped0 and a.object_id b.object_id and a.[type] in (P,V,AF) order by a.[name] ASC GO--1、查看所有存儲過程與函數 exec sp_store…

UDP數據包的大小

問題來源于日志信息&#xff0c;在這里總結一下&#xff0c;后續在補充新的內容。在鏈路層&#xff0c;由以太網的物理特性決定了數據幀的長度為&#xff08;46&#xff0b;18&#xff09;---&#xff08;1500&#xff0b;18&#xff09;&#xff0c;其中的18是鏈路層的首部和尾…

博科查看光功率_法拉第旋光器:非互易性旋轉光的偏振

法拉第旋光器是利用法拉第效應制作的光學器件&#xff0c;當入射光正向(或反向)進入旋光器時&#xff0c;入射光偏振面會發生旋轉。法拉第效應1845年&#xff0c;法拉第發現&#xff1a;當一束平面偏振光通過置于磁場中的磁光介質時&#xff0c;平面偏振光的偏振面就會隨著平行…

Object.prototype 原型和原型鏈

Object.prototype 原型和原型鏈 原型 Javascript中所有的對象都是Object的實例&#xff0c;并繼承Object.prototype的屬性和方法&#xff0c;有些屬性是隱藏的。換句話說&#xff0c;在對象創建時會存在預定義的屬性&#xff0c;其中有一個屬性就是原型對象。在函數對象中存在原…

leetcode 406. 根據身高重建隊列(貪心算法)

假設有打亂順序的一群人站成一個隊列。 每個人由一個整數對 (h, k) 表示&#xff0c;其中 h 是這個人的身高&#xff0c;k 是應該排在這個人前面且身高大于或等于 h 的人數。 例如&#xff1a;[5,2] 表示前面應該有 2 個身高大于等于 5 的人&#xff0c;而 [5,0] 表示前面不應該…