<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          “覆蓋率檢測”的實現(xiàn)原理,就這?

          共 2233字,需瀏覽 5分鐘

           ·

          2021-10-17 23:16

          覆蓋率檢測是用來判斷單測完整性的,jest 和 karma 都提供了這種功能:

          覆蓋率就是執(zhí)行過的代碼占總代碼的比例,比如執(zhí)行了多少行(Line),執(zhí)行了多少個分支(Branch),執(zhí)行了多少個函數(shù)(Function),執(zhí)行了多少條語句(Statement)。

          用它比上總的數(shù)量就是覆蓋率,分為行覆蓋率、分支覆蓋率、函數(shù)覆蓋率、語句覆蓋率等。

          看起來是不是很神奇,執(zhí)行完一遍就能知道覆蓋到了哪些代碼,其實實現(xiàn)原理比較簡單,相信看完這篇文章,你會有“就這?”的感覺。

          原理探究

          jest 和 karama 都是基于 istanbul 做的覆蓋率檢測,我們來探究下 istanbul 的實現(xiàn)原理。

          測試代碼如下:

          我們執(zhí)行 istanbul 的 instrument 命令:

          npx?istanbul?instrument?./test.js?-o?./out.js

          instrument 是指函數(shù)插樁,也就是透明的給函數(shù)添加一些代碼。

          為什么要插樁呢?看完生成的代碼你就明白了。

          我們來格式化一下,把變量名替換下。

          這就是轉換后的代碼,在每一個 statement,每一個 function、每一個 branch 都做了計數(shù),分別是 s、f、b 屬性。

          上面還有一段代碼:

          初始化了全局變量 AAA,記錄了這些信息:

          • path:路徑
          • s:statement 數(shù)
          • b:branch 數(shù)
          • f:function 數(shù)
          • fnMap:function 的開始結束位置信息
          • statementMap:statement 的開始結束位置信息
          • branchMap:branch 的開始結束位置信息

          看到這里我們大概就能搞懂覆蓋率的原理了,就是對每個 statement、function、branch 都插入一段計數(shù)代碼,記錄在一個全局對象中。

          為了不和別的全局變量沖突,這個對象的名字是隨機生成的,比如?__cov_5ZoEXQ_Hbo27uXArxdm2oA,這里為了簡化改為了 AAA。

          我們搞明白了覆蓋率就是靠插入計數(shù)代碼,那怎么做的插樁呢?

          函數(shù)插樁

          函數(shù)插樁是基于 AST,找到 statement、function、branch 的 AST,在前面插入插樁代碼的 AST。

          istanbul 確實也是這么做的。

          下面是 istanbul 的源碼(只看紅線標出的位置就行):

          就是通過 esprima(js parser)來把代碼 parse 成 AST,然后對 AST 進行插樁。

          插樁代碼分為兩部分,一部分是初始化全局對象的代碼,一部分是每個分支、語句、函數(shù)的計數(shù)代碼。

          我們分別來看下:

          初始化全局對象的代碼插樁

          istanbul 初始化了全局的 coverState 對象用于統(tǒng)計:

          做插樁的時候會記錄信息到這個 coverState 中:

          最后把 coverState 變成字符串加入到代碼里:

          那具體的分支、語句、函數(shù)的 AST 是怎么插樁的?

          分支、語句、函數(shù)的插樁

          對不同 AST 的插樁,就是遍歷過程中根據(jù)類型做不同的處理:

          然后,具體的插樁就是在前面插入一段 AST:

          statement 插樁:

          function 插樁:

          看到這里,我們就知道了函數(shù)插樁的實現(xiàn)原理,就是遍歷 AST,在不同的位置插入計數(shù)代碼的 AST 就可以了。

          但是有的同學可能會說了,平時我也沒手動生成插樁后的代碼啊?用 jest --coverage 跑測試用例自動就做了計數(shù),然后給出覆蓋率數(shù)據(jù)了。

          istanbul 是怎么做到透明的插樁的呢?

          require hook 實現(xiàn)透明無感知的函數(shù)插樁

          看過之前一篇 require hook 的魔術那篇文章的小伙伴知道,nodejs 的模塊加載是分為 load、extension['.js']、compile 這幾步的。

          我們只需要重寫 extension['.js'] 這一步,就能做到透明的代碼轉換。

          istanbul 也是這么做的:

          它就是通過修改了 extension['.js'] 方法,在這里面做了函數(shù)插樁,之后執(zhí)行的代碼就是轉換過后的了,開發(fā)者根本感知不到。

          總結

          jest 和 karma 都基于 istanbul 實現(xiàn)了覆蓋率檢測。覆蓋率統(tǒng)計的原理就是函數(shù)插樁,基于 AST 在代碼的 statement、function、branch 處插入計數(shù)代碼,同時通過 require hook 實現(xiàn)了透明的轉換。這樣代碼一執(zhí)行就能拿到統(tǒng)計數(shù)據(jù),自然就可以算出覆蓋率了。

          看完之后,是不是覺得:

          覆蓋率檢測的實現(xiàn),就這?



          往期推薦



          解密初、中、高級程序員的進化之路(前端)


          程序員一定會有35歲危機嗎?


          近 20k Star的項目說不做就不做了,但總結的內(nèi)容值得借鑒


          但凡早知道這28個網(wǎng)站,都不至于學得那么不扎實





          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)

          2. 歡迎加我微信「huab119」拉你進技術群,長期交流學習...

            關注公眾號「前端勸退師」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。



          點個在看支持我吧,轉發(fā)就更好了

          如果覺得這篇文章還不錯,來個【轉發(fā)、收藏、在看】三連吧,讓更多的人也看到~



          瀏覽 76
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人人草人 | 中文字幕一区二区三区乱码 | 亚洲精品久久久久久久久久久久久久 | 国产一区二区三区18 | 免费看在线黄色 |