在 Node.js 中收集代码覆盖率
¥Collecting code coverage in Node.js
Node.js 通过其测试运行器提供对代码覆盖率的内置支持,可以使用 --experimental-code-coverage
标志启用该支持。
¥Node.js provides built-in support for code coverage through its test runner, which can be enabled using the --experimental-code-coverage
flag.
如果使用 run()
API,则必须将 coverage
选项设置为 true
。有关 run()
API 的更多信息,请参阅 node:test
文档。
¥If using the run()
API, the coverage
option must be set to true
. For more information on the run()
API, see the node:test
documentation.
什么是代码覆盖率?
¥What is code coverage?
代码覆盖率是测试运行器的一个指标,用于衡量在测试期间执行了多少程序源代码。它揭示了代码库的哪些部分经过测试,哪些没有经过测试,有助于查明测试套件中的差距。这可确保对软件进行更全面的测试,并最大限度地降低未检测到错误的风险。通常以百分比表示,代码覆盖率百分比越高,测试覆盖率越全面。有关代码覆盖率的更详细说明,你可以参考 "代码覆盖率" 维基百科文章。
¥Code coverage is a metric for test runners that gauges how much of a program’s source code is executed during testing. It reveals which portions of the codebase are tested and which are not, helping to pinpoint gaps in the test suite. This ensures more comprehensive testing of the software and minimizes the risk of undetected bugs. Typically expressed as a percentage, higher code coverage percentages indicate more thorough test coverage. For a more detailed explanation of code coverage, you can refer to the "Code coverage" Wikipedia article.
基本覆盖率报告
¥Basic coverage reporting
让我们通过一个简单的示例来演示代码覆盖率在 Node.js 中的工作原理。
¥Let's walk through a simple example to demonstrate how code coverage works in Node.js.
注意:此示例以及此文件中的所有其他示例均使用 CommonJS 编写。如果你不熟悉这个概念,请阅读 CommonJS 模块 文档。
¥Note: This example, and all other ones in this file, are written using CommonJS. If you are unfamiliar with this concept, please read the CommonJS Modules documentation.
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
。
¥In the module, we have three functions: add
, isEven
, and multiply
.
在测试文件中,我们正在测试 add()
和 isEven()
函数。请注意,multiply()
函数未被任何测试覆盖。
¥In the test file, we are testing the add()
and isEven()
functions. Notice that the multiply()
function is not covered by any tests.
要在运行测试时收集代码覆盖率,请参阅以下代码片段:
¥To collect code coverage while running your tests, see the following snippets:
node --experimental-test-coverage --test main.test.js
运行测试后,你将收到一份如下所示的报告:
¥After running the tests, you'll receive a report that looks something like this:
✔ 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
覆盖率报告提供了测试覆盖代码量的细目:
¥The coverage report provides a breakdown of how much of your code is covered by tests:
-
行覆盖率:测试期间执行的行的百分比。
¥Line Coverage: The percentage of lines executed during the tests.
-
分支覆盖率:测试的代码分支(如 if-else 语句)的百分比。
¥Branch Coverage: The percentage of code branches (like if-else statements) tested.
-
函数覆盖率:测试期间调用的函数的百分比。
¥Function Coverage: The percentage of functions that have been invoked during testing.
在此示例中:
¥In this example:
-
main.js
显示 76.92% 的行覆盖率和 66.67% 的函数覆盖率,因为未测试multiply()
函数。未覆盖的行(9-11)对应于此函数。¥
main.js
shows 76.92% line coverage and 66.67% function coverage because themultiply()
function was not tested. The uncovered lines (9-11) correspond to this function. -
main.test.js
显示所有指标的覆盖率为 100%,表明测试本身已完全执行。¥
main.test.js
shows 100% coverage across all metrics, indicating that the tests themselves were fully executed.
包括和排除
¥Including and excluding
在处理应用时,你可能会遇到需要排除某些文件或代码行的情况。
¥When working on applications, you might encounter situations where certain files or lines of code need to be excluded.
Node.js 提供了处理此问题的机制,包括使用注释忽略特定代码部分和使用 CLI 排除整个模式。
¥Node.js provides mechanisms to handle this, including the use of comments to ignore specific code sections and the CLI to exclude entire patterns.
使用注释
¥Using comments
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)已被忽略。
¥When reporting coverage with this modified main.js
file, the report will now show 100% coverage across all metrics. This is because the uncovered lines (9-11) have been ignored.
有多种方法可以使用注释来忽略代码段。
¥There are multiple ways to ignore sections of code using comments.
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%。
¥Each of these different methods will produce the same report, with 100% code coverage across all metrics.
使用 CLI
¥Using the CLI
Node.js 提供了两个 CLI 参数来管理覆盖率报告中特定文件的包含或排除。
¥Node.js offers two CLI arguments for managing the inclusion or exclusion of specific files in a coverage report.
--test-coverage-include
标志(run()
API 中的 coverageIncludeGlobs
)将覆盖范围限制为与提供的 glob 模式匹配的文件。默认情况下,/node_modules/
目录中的文件被排除在外,但此标志允许你明确包含它们。
¥The --test-coverage-include
flag (coverageIncludeGlobs
in the run()
API) restricts the coverage to files that match the provided glob pattern. By default, files in the /node_modules/
directory are excluded, but this flag allows you to explicitly include them.
--test-coverage-exclude
标志(run()
API 中的 coverageExcludeGlobs
)从覆盖率报告中省略与给定 glob 模式匹配的文件。
¥The --test-coverage-exclude
flag (coverageExcludeGlobs
in the run()
API) omits files that match the given glob pattern from the coverage report.
这些标志可以多次使用,当两者一起使用时,文件必须遵守包含规则,同时避免排除规则。
¥These flags can be used multiple times, and when both are used together, files must adhere to the inclusion rules, while also avoiding the exclusion rules.
.
├── main.test.js
├── src
│ ├── age.js
│ └── name.js
src/age.js
在上述报告中的覆盖率不够理想,但使用 --test-coverage-exclude
标志(run()
API 中的 coverageExcludeGlobs
),可以将其完全排除在报告之外。
¥src/age.js
has less-than-optimal coverage in the report above, but with the --test-coverage-exclude
flag (coverageExcludeGlobs
in the run()
API), it can be excluded from the report entirely.
node --experimental-test-coverage --test-coverage-exclude=src/age.js --test main.test.js
我们的测试文件也包含在此覆盖率报告中,但我们只希望 src/
目录中的 JavaScript 文件。在这种情况下可以使用 --test-coverage-include
标志(run()
API 中的 coverageIncludeGlobs
)。
¥Our test file is also included in this coverage report, but we only want JavaScript files in the src/
directory. The --test-coverage-include
flag (coverageIncludeGlobs
in the run()
API) can be used in this case.
node --experimental-test-coverage --test-coverage-include=src/*.js --test main.test.js
阈值
¥Thresholds
默认情况下,当所有测试通过时,Node.js 会以代码 0
退出,这表示执行成功。但是,当覆盖率失败时,可以配置覆盖率报告以代码 1
退出。
¥By default, when all tests pass, Node.js exits with code 0
, which indicates a successful execution. However, the coverage report can be configured to exit with code 1
when coverage is failing.
Node.js 当前支持所有三种支持的覆盖率的阈值:
¥Node.js currently supports thresholds for all three of the coverages supported:
-
--test-coverage-lines
(run()
API 中的lineCoverage
)用于行覆盖。¥
--test-coverage-lines
(lineCoverage
in therun()
API) for line coverage. -
--test-coverage-branches
(run()
API 中的branchCoverage
)用于分支覆盖。¥
--test-coverage-branches
(branchCoverage
in therun()
API) for branch coverage. -
--test-coverage-functions
(run()
API 中的functionCoverage
)用于函数覆盖。¥
--test-coverage-functions
(functionCoverage
in therun()
API) for function coverage.
如果你希望要求上一个示例的行覆盖率 >= 90%,则可以使用 --test-coverage-lines=90
标志(run()
API 中的 lineCoverage: 90
)。
¥If you wanted to require the previous example to have line coverage >= 90%, you could use the --test-coverage-lines=90
flag (lineCoverage: 90
in the run()
API).
node --experimental-test-coverage --test-coverage-lines=90 --test main.test.js