程式碼覆蓋率命令列工具

GraalVM 提供程式碼覆蓋率命令列工具,讓使用者可以記錄並分析程式碼特定執行時的原始碼覆蓋率。

程式碼覆蓋率,以原始碼行數、函式或陳述式覆蓋的百分比表示,是了解特定原始碼執行的重要指標,通常與測試品質 (測試覆蓋率) 相關聯。為單行程式碼提供視覺化的覆蓋率概觀,讓開發人員了解哪些程式碼路徑已涵蓋,哪些尚未涵蓋,深入了解執行的特性,例如,可以作為進一步測試工作的依據。

以下範例應用程式將用於示範 GraalVM 的程式碼覆蓋率功能。此應用程式定義了一個 getPrime 函式,使用基於埃拉托斯特尼篩法演算法的基本質數計算器來計算第 n 個質數。它還具有前 20 個質數的簡陋快取。

  1. 將以下程式碼複製到名為 primes.js 的新檔案中
class AcceptFilter {
    accept(n) {
        return true
    }
}
class DivisibleByFilter {
    constructor(number, next) {
        this.number = number;
        this.next = next;
    }
    accept(n) {
        var filter = this;
        while (filter != null) {
            if (n % filter.number === 0) {
                    return false;
            }
            filter = filter.next;
        }
        return true;
    }
}
class Primes {
    constructor() {
        this.number = 2;
        this.filter = new AcceptFilter();
    }
    next() {
        while (!this.filter.accept(this.number)) {
            this.number++;
        }
        this.filter = new DivisibleByFilter(this.number, this.filter);
        return this.number;
    }
}
function calculatePrime(n) {
    var primes = new Primes();
    var primesArray = [];
    for (let i = 0; i < n; i++) {
        primesArray.push(primes.next());
    }
    return primesArray[n-1];
}
function getPrime(n) {
    var cache = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71];
    var n = arguments[0];
    if (n > cache.length) { return calculatePrime(n); }
    return cache[n-1];
}
// TESTS
console.assert(getPrime(1) == 2);
console.assert(getPrime(10) == 29);

請注意,最後幾行是斷言,將其視為單元測試。

  1. 執行 js primes.js。範例應用程式應該不會列印任何輸出,因為所有斷言都通過了。但是斷言對實作的測試有多徹底呢?

  2. 執行 js primes.js --coverage 以啟用程式碼覆蓋率。程式碼覆蓋率工具應為範例應用程式列印如下輸出
    js primes.js --coverage
    --------------------------------------------------------
    Code coverage histogram.
    Shows what percent of each element was covered during execution
    --------------------------------------------------------
     Path               |  Statements |    Lines |    Roots
    --------------------------------------------------------
     /path/to/primes.js |      20.69% |   26.67% |   22.22%
    --------------------------------------------------------
    

    追蹤器會為每個原始檔列印覆蓋率直方圖。您可以看到陳述式覆蓋率約為 20%,行覆蓋率約為 26%,而根覆蓋率 (術語「根」涵蓋函式、方法等) 為 22.22%。這告訴您,我們的簡單測試並不太擅長執行原始碼。接下來您將找出程式碼的哪些部分未涵蓋。

  3. 執行 js primes.js --coverage --coverage.Output=detailed。準備好看到一些冗長的輸出。將輸出指定為 detailed 將列印所有原始碼行,並在開頭加上覆蓋率註釋。由於輸出可能很大,建議將此輸出模式與 --coverage.OutputFile 選項結合使用,將輸出直接列印到檔案。我們的範例應用程式的輸出如下
js primes.js --coverage --coverage.Output=detailed
--------------------------------------------------------
Code coverage per line of code and what percent of each element was covered during execution (per source)
  + indicates the line is covered during execution
  - indicates the line is not covered during execution
  p indicates the line is part of a statement that was incidentally covered during execution
    for example, a not-taken branch of a covered if statement
--------------------------------------------------------
 Path               |  Statements |    Lines |    Roots
 /path/to/primes.js |      20.69% |   26.67% |   22.22%

  class AcceptFilter {
      accept(n) {
-         return true
      }
  }
  class DivisibleByFilter {
      constructor(number, next) {
-         this.number = number;
-         this.next = next;
      }
      accept(n) {
-         var filter = this;
-         while (filter != null) {
-             if (n % filter.number === 0) {
-                     return false;
-             }
-             filter = filter.next;
          }
-         return true;
      }
  }
  class Primes {
      constructor() {
-         this.number = 2;
-         this.filter = new AcceptFilter();
      }
      next() {
-         while (!this.filter.accept(this.number)) {
-             this.number++;
          }
-         this.filter = new DivisibleByFilter(this.number, this.filter);
-         return this.number;
      }
  }
  function calculatePrime(n) {
-     var primes = new Primes();
-     var primesArray = [];
-     for (let i = 0; i < n; i++) {
-         primesArray.push(primes.next());
      }
-     return primesArray[n-1];
  }
  function getPrime(n) {
+     var cache = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71];
+     var n = arguments[0];
p     if (n > cache.length) { return calculatePrime(n); }
+     return cache[n-1];
  }
  // TESTS
+ console.assert(getPrime(1) == 2);
+ console.assert(getPrime(10) == 29);
--------------------------------------------------------

正如輸出開頭的圖例所解釋的,執行涵蓋的行會以 + 開頭。未涵蓋的行會以 - 開頭。部分涵蓋的行會以 p 開頭 (例如,當 if 陳述式被涵蓋,但只採用一個分支時,請考慮將另一個分支視為偶然涵蓋)。

查看輸出,您可以看到 calculatePrime 函式及其所有呼叫都永遠不會執行。再次查看斷言和 getPrime 函式,很明顯我們的測試始終會命中快取。因此,大部分程式碼永遠不會執行。您可以改進這一點。

  1. primes.js 檔案的末尾新增 console.assert(getPrime(30) == 113); 並執行 js primes.js --coverage。由於新增的斷言會以 30 呼叫 getPrime (我們的快取只有 20 個條目),覆蓋率將如下所示
js primes.js --coverage
-------------------------------------------------------
Code coverage histogram.
  Shows what percent of each element was covered during execution
-------------------------------------------------------
 Path               |  Statements |    Lines |    Roots
-------------------------------------------------------
 /path/to/primes.js |     100.00% |  100.00% |  100.00%
-------------------------------------------------------

與其他工具整合 #

程式碼覆蓋率工具提供與其他工具整合的方法。使用 --coverage.Output=lcov 執行會以常用的 lcov 格式產生輸出,該格式由多個工具 (例如,genhtml) 用於顯示覆蓋率資料。請參閱下一個範例,其中顯示如何使用 Visual Studio Code 可視化 Node.js 應用程式的覆蓋率。

  1. 將以下程式碼複製到名為 nodeapp.js 的新檔案中
const express = require('express')
const app = express()
const port = 3000

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.get('/neverCalled', (req, res) => {
  res.send('You should not be here')
})

app.get('/shutdown', (req, res) => {
  process.exit();
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
  1. 安裝 express 模組相依性
    $JAVA_HOME/bin/npm install express
    
  2. 啟動 Visual Studio Code 並安裝支援 lcov 的程式碼覆蓋率外掛程式。此範例使用 程式碼覆蓋率醒目提示器,但其他外掛程式也應以類似方式運作。

  3. 啟用並設定覆蓋率後,執行 nodeapp.js 檔案
    $JAVA_HOME/bin/node --coverage --coverage.Output=lcov \
    --coverage.OutputFile=coverage/lcov.info \
    nodeapp.js
    

請注意,程式碼覆蓋率醒目提示器外掛程式預設會在 coverage 目錄中尋找 lcov.info 檔案,因此請將程式碼覆蓋率工具的輸出導向該目錄。

  1. 在瀏覽器中瀏覽 localhost:3000/,然後瀏覽 localhost:3000/shutdown 以關閉應用程式。

  2. 開啟 Visual Studio Code,然後開啟包含 nodeapp.js 檔案和 coverage 目錄的資料夾,您應該會看到類似以下的影像

Visual Studio Code Coverage

如果您希望將 GraalVM 程式碼覆蓋率工具收集的資料與您自己的視覺化整合,--coverage.Output=json 選項會導致輸出為 JSON 檔案,其中包含追蹤器收集的原始資料。

與我們聯繫