在 Node.js 中收集代码覆盖率
Node.js 通过其测试运行器提供对代码覆盖率的内置支持,可以使用 --experimental-code-coverage
标志启用。
如果使用 run()
API,则必须将 coverage
选项设置为 true
。有关 run()
API 的更多信息,请参阅 node:test
文档。
什么是代码覆盖率?
代码覆盖率是测试运行器的一项指标,用于衡量在测试期间执行的程序源代码量。它揭示了代码库中哪些部分经过了测试,哪些部分没有经过测试,从而有助于查明测试套件中的差距。这确保了对软件进行更全面的测试,并最大限度地降低了未检测到错误的风险。通常以百分比表示,较高的代码覆盖率百分比表示更彻底的测试覆盖率。有关代码覆盖率的更详细说明,您可以参考 “代码覆盖率”维基百科文章。
基本覆盖率报告
让我们来看一个简单的例子,演示代码覆盖率在 Node.js 中是如何工作的。
注意:此文件中的此示例和所有其他示例均使用 CommonJS 编写。如果您不熟悉此概念,请阅读 CommonJS 模块 文档。
function add(a, b) {
return a + b;
}
function isEven(num) {
return num % 2 === 0;
}
function multiply(a, b) {
return a * b;
}
module.exports = { add, isEven, multiply };
在该模块中,我们有三个函数: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 提供了处理此问题的机制,包括使用注释来忽略特定代码段以及使用 CLI 来排除整个模式。
使用注释
function add(a, b) {
return a + b;
}
function isEven(num) {
return num % 2 === 0;
}
/* node:coverage ignore next 3 */
function multiply(a, b) {
return a * b;
}
module.exports = { add, isEven, multiply };
在使用此修改后的 main.js
文件报告覆盖率时,该报告现在将显示所有指标的 100% 覆盖率。这是因为未覆盖的行 (9-11) 已被忽略。
有多种方法可以使用注释来忽略代码段。
function add(a, b) {
return a + b;
}
function isEven(num) {
return num % 2 === 0;
}
/* node:coverage ignore next 3 */
function multiply(a, b) {
return a * b;
}
module.exports = { add, isEven, multiply };
这些不同的方法都将生成相同的报告,其中所有指标的代码覆盖率均为 100%。
使用 CLI
Node.js 提供了两个 CLI 参数,用于管理覆盖率报告中特定文件的包含或排除。
--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