多程式下的測試覆蓋率

發表於2015-12-15

多程式下的測試覆蓋率

單元測試在 Node.js 專案開發中的重要性就不言而喻了,專案一旦稍微大起來了就經常出現拆東牆補西牆的情況。這邊修復了一個 bug,那邊又不知道什麼時候產生了一個新的 bug,越到後面沒有經過完整的測試都不敢隨便釋出。

程式碼覆蓋率

測試的時候,我們常常關心,是否所有程式碼都測試到了。這個指標就叫做“程式碼覆蓋率”(code coverage),它有四個測量維度。

  • 行覆蓋率(line coverage):是否每一行都執行了?
  • 函式覆蓋率(function coverage):是否每個函式都呼叫了?
  • 分支覆蓋率(branch coverage):是否每個 if 程式碼塊都執行了?
  • 語句覆蓋率(statement coverage):是否每個語句都執行了?

目前在 Node.js 開發中比較流行的測試覆蓋率工具是 Istanbul。

Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests and browser tests. Built for scale.

Istanbul 不但可以統計到整個專案的程式碼覆蓋率,還會生成一份漂亮的覆蓋率報告,準確的標記出哪些程式碼沒有被覆蓋到。

平常我們寫的 JS 測試用例大部分都是單程式的場景,下面我們來看一個多程式專案的測試情況又是怎麼樣的呢?

多程式 demo

先寫一個簡單的 demo,使用 Mocha 做單元測試,Istanbul 生成測試覆蓋率。
下面是整個專案的目錄結構。

master.js

worker.js

上面的 demo 實現了一個簡單的程式間 rpc 功能,master 程式提供介面,worker 程式實現具體的邏輯,並通過程式間通訊給 master 呼叫。

worker 程式 向 master 程式註冊了 add 和 time 方法,分別提供相加和相乘的服務。

測試用例

我們接著使用 Mocha 寫一個指令碼測試下這個功能。

index.test.js

執行 node --harmony node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha -- test/**/*.test.js 輸出

測試結果

所有的測試用例都已經跑通並統計出各種覆蓋率, 再看看生成的覆蓋率報告
覆蓋率報告,子程式沒有被統計到

發現並沒有 worker.js 的覆蓋率資料,所以上面輸出的覆蓋率是不完整的。

分析原因

為什麼測試的結果中會沒有 worker.js 的覆蓋率資料呢,稍微想一下其實很簡單,master.js 之所以有覆蓋率資料因為它通過 Istanbul 啟動執行的,程式碼執行之前 Istanbul 會對 master.js 進行 instrument。下面是一段程式碼被 instrument 前後的情況。

before instrument

after instrument

可以看出 instrument 後的程式碼每一行是否被執行都可以監測到,而 worker.js 是 master.js 通過呼叫 childProcess.fork, 在一個很乾淨的 Node.js 環境中執行, 執行的程式碼沒有被 instrument,執行情況自然無法被 Istanbul 檢測到。

解決方案

所以要獲取 worker.js 的覆蓋率,必須在執行 worker.js 程式碼之前先注入 Istanbul,那很自然的就會想到 hack 掉 childProcess.fork。

雖然這樣處理後 master.js 和 worker.js 的覆蓋率都有了,但由於它們是在不同的程式中產生的,Istanbul 不會自動將 2 個檔案的覆蓋率資料合併處理,所以我們可以先產生覆蓋率資料,再根據覆蓋率資料生成報告。由於涉及到多個程式,啟動 Istanbul 時需要加上include-pid 引數,這樣每個程式生成的 coverage.json 檔案就會帶上程式 pid,否則 子程式的 coverage.json 會覆蓋掉 主程式的。

執行 istanbul report --root ./coverage text-summary json lcov 便會自動對生成的 coverage-pid.json 檔案合併處理,產生最終的覆蓋率資料以及覆蓋率報告。

最後將這 2 條命令整合到 Node.js 專案的 package.json 檔案中。

執行 npm test
主程式和子程式中的所有程式碼覆蓋率都被統計到
可以看到主程式和子程式中的所有程式碼覆蓋率都被統計到了。

相關文章