- 适用于 JDK 23 的 GraalVM(最新版)
- 适用于 JDK 24 的 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
函数,可以清楚地看到,我们的测试始终命中缓存。因此,大部分代码从未执行。您可以对此进行改进。
- 在
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 应用程序的覆盖率。
- 将以下代码复制到一个名为
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 的代码覆盖率插件。代码覆盖率高亮显示 用于本示例,但其他插件应该也能类似地工作。
- 使用启用的覆盖率和配置运行nodeapp.js 文件
$JAVA_HOME/bin/node --coverage --coverage.Output=lcov \ --coverage.OutputFile=coverage/lcov.info \ nodeapp.js
请注意,代码覆盖率高亮显示插件默认情况下会在coverage
目录中查找lcov.info
文件,因此将代码覆盖率工具的输出直接定向到该目录。
-
在浏览器中访问localhost:3000/,然后访问localhost:3000/shutdown 关闭应用程序。
-
打开 Visual Studio Code,然后打开包含
nodeapp.js
文件和coverage
目录的文件夹,您应该会看到类似以下图像的图像
如果您想将 GraalVM 代码覆盖率工具收集的数据与您自己的可视化工具集成,--coverage.Output=json
选项会将输出作为包含跟踪器收集的原始数据的 JSON 文件。