- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
代码覆盖率命令行工具
GraalVM 提供了一个**代码覆盖率命令行工具**,允许用户记录和分析特定代码执行的源代码覆盖率。
代码覆盖率作为源代码行、函数或语句的覆盖百分比,是理解特定源代码执行的重要指标,并且通常与测试质量(测试覆盖率)相关联。为每行代码提供可视化覆盖率概览,可以向开发者展示哪些代码路径被覆盖,哪些没有被覆盖,从而深入了解执行的特性,例如,为进一步的测试工作提供参考。
以下示例应用程序将用于演示 GraalVM 的代码覆盖率功能。此应用程序定义了一个 getPrime
函数,该函数使用基于埃拉托斯特尼筛法算法的基本素数计算器来计算第 n 个素数。它还有一个稍微有些简单的、包含前 20 个素数的缓存。
- 将以下代码复制到一个名为
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);
请注意,最后几行是应被视为单元测试的断言。
-
运行
js primes.js
。示例应用程序应该不会打印任何输出,因为所有断言都通过了。但是,这些断言对实现的测试效果如何呢? - 运行
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%。这表明我们简单的测试在覆盖源代码方面表现不佳。接下来您将找出代码中哪些部分没有被覆盖。
- 运行
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
函数,很明显我们的测试总是命中缓存。因此,大部分代码从未被执行。您可以对此进行改进。
- 将
console.assert(getPrime(30) == 113);
添加到primes.js
文件的末尾,并运行js primes.js --coverage
。由于新添加的断言调用getPrime
时传入了 30(我们的缓存只有 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 应用程序的覆盖率。
- 将以下代码复制到一个名为
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}!`))
- 安装 express 模块依赖项
$JAVA_HOME/bin/npm install express
-
启动 Visual Studio Code 并安装一个支持 lcov 的代码覆盖率插件。本示例使用 Code Coverage Highlighter,但其他插件也应该类似工作。
- 运行 nodeapp.js 文件,并启用和配置覆盖率
$JAVA_HOME/bin/node --coverage --coverage.Output=lcov \ --coverage.OutputFile=coverage/lcov.info \ nodeapp.js
请注意,Code Coverage Highlighter 插件默认在 coverage
目录中查找 lcov.info
文件,因此请将代码覆盖率工具的输出定向到该目录。
-
在浏览器中访问 localhost:3000/,然后访问 localhost:3000/shutdown 以关闭应用程序。
-
打开 Visual Studio Code,然后打开包含
nodeapp.js
文件和coverage
目录的文件夹,您应该会看到类似以下的图像:
如果您希望将 GraalVM 代码覆盖率工具收集的数据与您自己的可视化工具集成,--coverage.Output=json
选项将使输出成为一个包含跟踪器收集的原始数据的 JSON 文件。