這個研究起源于這個文章 如何收集 nodejs 服務端的測試覆寫率(Nodejs), 由于自己之前對這塊其實是沒有研究過的,是以特地花了些時間去了解了下。
知識儲備
雖然說沒有做過這塊的覆寫率研究,但是對于針對nodejs這塊的覆寫率工具之前是有做過一定的了解的,主要是在 nyc 以及 istanbul-middleware (以下簡稱IM)。
關于兩者的一些說明可以去看下這兩篇文章,裡面講了挺多内容的 探索istanbul/nyc代碼覆寫工具的原理 React Native 代碼覆寫率擷取探索 (二)
nyc的初步嘗試
由于nodejs這塊很少會用到babel編譯這種方式,是以我們就放棄了之前 聊聊前端代碼覆寫率 (長文慎入) 介紹到的 babel-plugin-istanbul。 而是直接采用nyc運作前插樁的方式。
這裡可以直接檢視下這個栗子 nyc-expresss-coverage-demo
其實就是通過
nyc instrument
的指令 對相應的代碼目錄進行插樁操作。 相應的插樁後的檔案如:
data.js
module.exports = {
authors: [
{
id: '1',
name: 'John Irving',
country: 'USA',
dob: '03/02/1942'
},
{
id: '2',
name: 'Gabriel Garcia Marquez',
country: 'Colombia',
dob: '03/06/1927'
},
{
id: '3',
name: 'Salman Rushdie',
country: 'India',
dob: '06/19/1947'
},
{
id: '4',
name: 'Stanislaw Lem',
country: 'Poland',
dob: '09/12/1921',
deceased: true
}
]
};
變為了
function cov_106hx7e2f0() {
var path = "/Users/sai/projects/nyc-expresss-coverage-demo/server/data.js";
var hash = "ff103e96a45dea0c0d53a6e72b3c27f5cdf62f87";
var global = new Function("return this")();
var gcv = "__coverage__";
var coverageData = {
path: "/Users/sai/projects/nyc-expresss-coverage-demo/server/data.js",
statementMap: {
"0": {
start: {
line: 1,
column: 0
},
end: {
line: 29,
column: 2
}
}
},
fnMap: {},
branchMap: {},
s: {
"0": 0
},
f: {},
b: {},
_coverageSchema: "1a1c01bbd47fc00a2c39e90264f33305004495a9",
hash: "ff103e96a45dea0c0d53a6e72b3c27f5cdf62f87"
};
var coverage = global[gcv] || (global[gcv] = {});
if (!coverage[path] || coverage[path].hash !== hash) {
coverage[path] = coverageData;
}
var actualCoverage = coverage[path];
{
// @ts-ignore
cov_106hx7e2f0 = function () {
return actualCoverage;
};
}
return actualCoverage;
}
cov_106hx7e2f0();
cov_106hx7e2f0().s[0]++;
module.exports = {
authors: [{
id: '1',
name: 'John Irving',
country: 'USA',
dob: '03/02/1942'
}, {
id: '2',
name: 'Gabriel Garcia Marquez',
country: 'Colombia',
dob: '03/06/1927'
}, {
id: '3',
name: 'Salman Rushdie',
country: 'India',
dob: '06/19/1947'
}, {
id: '4',
name: 'Stanislaw Lem',
country: 'Poland',
dob: '09/12/1921',
deceased: true
}]
};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImRhdGEuanMiXSwibmFtZXMiOlsibW9kdWxlIiwiZXhwb3J0cyIsImF1dGhvcnMiLCJpZCIsIm5hbWUiLCJjb3VudHJ5IiwiZG9iIiwiZGVjZWFzZWQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFlWTs7Ozs7Ozs7OztBQWZaQSxNQUFNLENBQUNDLE9BQVAsR0FBaUI7QUFDYkMsRUFBQUEsT0FBTyxFQUFFLENBQ0w7QUFDSUMsSUFBQUEsRUFBRSxFQUFFLEdBRFI7QUFFSUMsSUFBQUEsSUFBSSxFQUFFLGFBRlY7QUFHSUMsSUFBQUEsT0FBTyxFQUFFLEtBSGI7QUFJSUMsSUFBQUEsR0FBRyxFQUFFO0FBSlQsR0FESyxFQU9MO0FBQ0lILElBQUFBLEVBQUUsRUFBRSxHQURSO0FBRUlDLElBQUFBLElBQUksRUFBRSx3QkFGVjtBQUdJQyxJQUFBQSxPQUFPLEVBQUUsVUFIYjtBQUlJQyxJQUFBQSxHQUFHLEVBQUU7QUFKVCxHQVBLLEVBYUw7QUFDSUgsSUFBQUEsRUFBRSxFQUFFLEdBRFI7QUFFSUMsSUFBQUEsSUFBSSxFQUFFLGdCQUZWO0FBR0lDLElBQUFBLE9BQU8sRUFBRSxPQUhiO0FBSUlDLElBQUFBLEdBQUcsRUFBRTtBQUpULEdBYkssRUFtQkw7QUFDSUgsSUFBQUEsRUFBRSxFQUFFLEdBRFI7QUFFSUMsSUFBQUEsSUFBSSxFQUFFLGVBRlY7QUFHSUMsSUFBQUEsT0FBTyxFQUFFLFFBSGI7QUFJSUMsSUFBQUEsR0FBRyxFQUFFLFlBSlQ7QUFLSUMsSUFBQUEsUUFBUSxFQUFFO0FBTGQsR0FuQks7QUFESSxDQUFqQiIsInNvdXJjZXNDb250ZW50IjpbIm1vZHVsZS5leHBvcnRzID0ge1xuICAgIGF1dGhvcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgICAgaWQ6ICcxJyxcbiAgICAgICAgICAgIG5hbWU6ICdKb2huIElydmluZycsXG4gICAgICAgICAgICBjb3VudHJ5OiAnVVNBJyxcbiAgICAgICAgICAgIGRvYjogJzAzLzAyLzE5NDInXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICAgIGlkOiAnMicsXG4gICAgICAgICAgICBuYW1lOiAnR2FicmllbCBHYXJjaWEgTWFycXVleicsXG4gICAgICAgICAgICBjb3VudHJ5OiAnQ29sb21iaWEnLFxuICAgICAgICAgICAgZG9iOiAnMDMvMDYvMTkyNydcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgICAgaWQ6ICczJyxcbiAgICAgICAgICAgIG5hbWU6ICdTYWxtYW4gUnVzaGRpZScsXG4gICAgICAgICAgICBjb3VudHJ5OiAnSW5kaWEnLFxuICAgICAgICAgICAgZG9iOiAnMDYvMTkvMTk0NydcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgICAgaWQ6ICc0JyxcbiAgICAgICAgICAgIG5hbWU6ICdTdGFuaXNsYXcgTGVtJyxcbiAgICAgICAgICAgIGNvdW50cnk6ICdQb2xhbmQnLFxuICAgICAgICAgICAgZG9iOiAnMDkvMTIvMTkyMScsXG4gICAgICAgICAgICBkZWNlYXNlZDogdHJ1ZVxuICAgICAgICB9XG4gICAgXVxufTtcblxuXG4iXX0=
如果了解一定的邏輯的話就會發現其實下來node服務執行以後,就可以通過
global['__coverage__']
擷取到相應的覆寫率資料。 再然後的内容就不繼續說了,可以看下前面提到的知識儲備中的文章,怎麼去生成報告。 或者看下demo中的步驟也是可以的。
IM的運作中插樁的嘗試
在上述的nyc的怎麼過程中我們發現整體的過程很不友善,首先是是編譯前插樁這個動作,再來就是覆寫率需要自己采集以及報告生成。
是以我們這個時候可以考慮下IM了。
IM 提供了以下的功能
- hook了
的方法,實際上就是被 require 的檔案都會插樁。require()
- 可以通過接口導出覆寫率的資料
- 允許重置覆寫率的資料
- 允許使用者下載下傳覆寫率的報告
這裡就不舉例說明怎麼使用IM的了,因為他的說明文檔已經很詳細了,關鍵就是使用
im.hookLoader()
這個api就可以了。
不過這裡有一個需要注意的地方, IM hook的是
require()
的方法, 是以這個會導緻一個問題就是引入IM庫的那個檔案沒辦法被插樁, 這個是需要注意的地方(這裡是個人嘗試總結的,如果有哪裡不對的,歡迎批評指出)。
另外在使用IM的時候還發現另外的一個問題, 那就是IM針對es6的插樁是存在問題的,類似于這個issue Updating Instrumentor (so that it supports ES6) ,其實隻要更換instrument的庫即可,不過作者已經不維護了,是以代碼也一直未合并。
nyc運作中插樁
經過上述的一些嘗試, 又重新把目光放回了nyc上,既然istanbul已經不維護,而轉到了nyc上,nyc這塊肯定不單單隻是運作前插樁這種方式的。是以嘗試又重新在網上搜尋了下,結果真的有了新的發現。 nodejs 測試覆寫度工具nyc(Istanbul)簡介 在這篇文章中作者執行nyc的方式并不是與mocha等測試架構結合使用的。而是直接在啟動服務指令增加了nyc。
帶着這個希望,重新嘗試了下,發現在啟動指令前 加上nyc執行以後,等到服務停止執行後,就可以在
coverage
目錄下生成覆寫率的報告了, 隻是這裡有一點不好的就是一定要服務停止以後才能夠生成報告。
是以我們需要友善些的方式: 就是支援實時動态生成覆寫率報告的, 是以就有了 nodejs-coverage-lib 這個庫, 其實這個庫的功能還是相當的簡單的,就是通過express啟動了一個服務,提供了一個報告下載下傳的接口, 而其中的過程則是擷取到全局的變量
__coverage__
存儲到對應的目錄下後,再直接通過nyc指令生成報告,壓縮最後的報告目錄提供下載下傳即可。
總結
以上就是關于nodejs這塊覆寫率的一些調研以及總結。
- 通過
運作前插樁的方式進行擷取到覆寫率資料,隻是步驟有點小麻煩nyc
- IM的運作時插樁的方式。隻是可能存在es6插樁的問題以及入口檔案無法擷取到覆寫率的問題
- nyc 運作時插樁的方式目前來看是最推薦的方式了。并且通過文中的第三方庫,可以省去很多過程中的步驟。
nodejs這塊的覆寫率這塊相對于前端來說還是會友善很多,因為主要還是覆寫率的資料就是在服務端處,免去了很多在使用者端采集的過程了。
PS: 這裡的覆寫率驗證隻是找了兩個項目做了嘗試,并不一定适用所有的項目(比如說typescript的項目)。