天天看點

關于 nodejs 覆寫率的探索

這個研究起源于這個文章 如何收集 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這塊覆寫率的一些調研以及總結。

  1. 通過

    nyc

    運作前插樁的方式進行擷取到覆寫率資料,隻是步驟有點小麻煩
  2. IM的運作時插樁的方式。隻是可能存在es6插樁的問題以及入口檔案無法擷取到覆寫率的問題
  3. nyc 運作時插樁的方式目前來看是最推薦的方式了。并且通過文中的第三方庫,可以省去很多過程中的步驟。

nodejs這塊的覆寫率這塊相對于前端來說還是會友善很多,因為主要還是覆寫率的資料就是在服務端處,免去了很多在使用者端采集的過程了。

PS: 這裡的覆寫率驗證隻是找了兩個項目做了嘗試,并不一定适用所有的項目(比如說typescript的項目)。

繼續閱讀