跳到内容

在 Node.js 中收集代码覆盖率

🌐 Collecting code coverage in Node.js

Node.js 通过其测试运行器提供对代码覆盖率的内置支持,可以使用 --experimental-test-coverage 标志启用。

🌐 Node.js provides built-in support for code coverage through its test runner, which can be enabled using the --experimental-test-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 模块 文档。

function (, ) {
  return  + ;
}

function () {
  return  % 2 === 0;
}

function (, ) {
  return  * ;
}

. = { , ,  };

在这个模块中,我们有三个函数:addisEvenmultiply

🌐 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:

  • 行覆盖率:测试过程中执行的代码行所占的百分比。
  • 分支覆盖率:已测试的代码分支(如 if-else 语句)所占的百分比。
  • 函数覆盖率:在测试过程中被调用的函数所占的百分比。

在这个例子中:

🌐 In this example:

  • main.js 显示 76.92% 的行覆盖率和 66.67% 的函数覆盖率,因为 multiply() 函数没有被测试。未覆盖的行(9-11)对应此函数。
  • main.test.js 在所有指标上显示了 100% 的覆盖率,这表明测试本身已被完全执行。

包含与排除

🌐 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 (, ) {
  return  + ;
}

function () {
  return  % 2 === 0;
}

/* node:coverage ignore next 3 */
function (, ) {
  return  * ;
}

. = { , ,  };

在使用此修改后的 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 (, ) {
  return  + ;
}

function () {
  return  % 2 === 0;
}

/* node:coverage ignore next 3 */
function (, ) {
  return  * ;
}

. = { , ,  };

每种不同的方法都会生成相同的报告,所有指标的代码覆盖率为 100%。

🌐 Each of these different methods will produce the same report, with 100% code coverage across all metrics.

使用命令行接口

🌐 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](https://nodejs.cn/api/cli.html#--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)可以将其完全从报告中排除。

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:

如果你想要求前面的示例具有 ≥ 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