在 Node.js 中收集代码覆盖率
Node.js 通过其测试运行器为代码覆盖率提供了内置支持,可以使用 --experimental-test-coverage
标志启用。
如果使用 run()
API,则必须将 coverage
选项设置为 true
。有关 run()
API 的更多信息,请参阅 node:test
文档。
什么是代码覆盖率?
代码覆盖率是测试运行器的一项指标,用于衡量程序源代码在测试期间被执行的程度。它揭示了代码库的哪些部分被测试了,哪些没有,有助于找出测试套件中的空白。这确保了对软件进行更全面的测试,并最大限度地降低了未被发现的错误的风险。通常以百分比表示,代码覆盖率越高表示测试覆盖越彻底。有关代码覆盖率的更详细解释,您可以参阅维基百科“代码覆盖率”条目。
基本覆盖率报告
让我们通过一个简单的例子来演示代码覆盖率在 Node.js 中是如何工作的。
注意:本示例以及本文件中的所有其他示例均使用 CommonJS 编写。如果您不熟悉此概念,请阅读 CommonJS 模块文档。
function (, ) {
return + ;
}
function () {
return % 2 === 0;
}
function (, ) {
return * ;
}
. = { , , };
在模块中,我们有三个函数:add
、isEven
和 multiply
。
在测试文件中,我们正在测试 add()
和 isEven()
函数。请注意,multiply()
函数未被任何测试覆盖。
要在运行测试时收集代码覆盖率,请参见以下代码片段
node --experimental-test-coverage --test main.test.js
运行测试后,您将收到一份如下所示的报告
✔ add() should add two numbers (1.505987ms)
✔ isEven() should report whether a number is even (0.175859ms)
ℹ tests 2
ℹ suites 0
ℹ pass 2
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 59.480373
ℹ start of coverage report
ℹ -------------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ -------------------------------------------------------------
ℹ main.js | 76.92 | 100.00 | 66.67 | 9-11
ℹ main.test.js | 100.00 | 100.00 | 100.00 |
ℹ -------------------------------------------------------------
ℹ all files | 86.96 | 100.00 | 80.00 |
ℹ -------------------------------------------------------------
ℹ end of coverage report
覆盖率报告详细说明了您的代码有多少被测试所覆盖
- 行覆盖率:测试期间执行的代码行百分比。
- 分支覆盖率:被测试的代码分支(如 if-else 语句)的百分比。
- 函数覆盖率:在测试期间被调用的函数百分比。
在此示例中
main.js
显示 76.92% 的行覆盖率和 66.67% 的函数覆盖率,因为multiply()
函数未被测试。未覆盖的行 (9-11) 对应于此函数。main.test.js
在所有指标上都显示了 100% 的覆盖率,这表明测试本身已完全执行。
包含和排除
在开发应用程序时,您可能会遇到需要排除某些文件或代码行的情况。
Node.js 提供了处理此问题的机制,包括使用注释来忽略特定的代码部分以及使用命令行排除整个模式。
使用注释
function (, ) {
return + ;
}
function () {
return % 2 === 0;
}
/* node:coverage ignore next 3 */
function (, ) {
return * ;
}
. = { , , };
使用修改后的 main.js
文件报告覆盖率时,报告现在将显示所有指标均为 100% 覆盖。这是因为未覆盖的行 (9-11) 已被忽略。
有多种方法可以使用注释来忽略代码部分。
function (, ) {
return + ;
}
function () {
return % 2 === 0;
}
/* node:coverage ignore next 3 */
function (, ) {
return * ;
}
. = { , , };
这些不同的方法都将生成相同的报告,所有指标的代码覆盖率均为 100%。
使用命令行
Node.js 提供了两个命令行参数,用于管理在覆盖率报告中包含或排除特定文件。
--test-coverage-include
标志(在 run()
API 中为 coverageIncludeGlobs
)将覆盖范围限制为与提供的 glob 模式匹配的文件。默认情况下,会排除 /node_modules/
目录中的文件,但此标志允许您明确地包含它们。
--test-coverage-exclude
标志(在 run()
API 中为 coverageExcludeGlobs
)会从覆盖率报告中忽略与给定 glob 模式匹配的文件。
这些标志可以多次使用,当两者同时使用时,文件必须遵守包含规则,同时也要避开排除规则。
.
├── main.test.js
├── src
│ ├── age.js
│ └── name.js
在上面的报告中,src/age.js
的覆盖率不甚理想,但使用 --test-coverage-exclude
标志(在 run()
API 中为 coverageExcludeGlobs
),可以将其完全从报告中排除。
node --experimental-test-coverage --test-coverage-exclude=src/age.js --test main.test.js
我们的测试文件也包含在此覆盖率报告中,但我们只想要 src/
目录中的 JavaScript 文件。在这种情况下,可以使用 --test-coverage-include
标志(在 run()
API 中为 coverageIncludeGlobs
)。
node --experimental-test-coverage --test-coverage-include=src/*.js --test main.test.js
阈值
默认情况下,当所有测试都通过时,Node.js 会以代码 0
退出,表示执行成功。但是,可以配置覆盖率报告,在覆盖率不达标时以代码 1
退出。
Node.js 目前支持为所有三种覆盖率设置阈值
--test-coverage-lines
(在run()
API 中为lineCoverage
)用于行覆盖率。--test-coverage-branches
(在run()
API 中为branchCoverage
)用于分支覆盖率。--test-coverage-functions
(在run()
API 中为functionCoverage
)用于函数覆盖率。
如果您想要求前一个示例的行覆盖率 >= 90%,您可以使用 --test-coverage-lines=90
标志(在 run()
API 中为 lineCoverage: 90
)。
node --experimental-test-coverage --test-coverage-lines=90 --test main.test.js