На этой странице

Node.js предоставляет встроенную поддержку покрытия кода через свой test runner, которую можно включить с помощью флага --experimental-test-coverage.

При использовании API run() опция coverage должна быть установлена в true. Больше информации об API run() см. в документации node:test.

Покрытие кода (code coverage) — это метрика для test runner'ов, оценивающая, какая часть исходного кода программы выполняется во время тестирования. Оно показывает, какие части кодовой базы протестированы, а какие нет, помогая точно определить пробелы в наборе тестов. Это обеспечивает более всестороннее тестирование ПО и минимизирует риск необнаруженных багов. Обычно выражается в процентах; более высокие проценты покрытия указывают на более тщательное покрытие тестами. Более подробное объяснение покрытия кода можно найти в статье «Code coverage» в Википедии.

Пройдёмся по простому примеру, чтобы показать, как работает покрытие кода в Node.js.

Примечание: этот пример, как и все остальные в этом файле, написан с использованием CommonJS. Если вам не знакома эта концепция, прочитайте документацию CommonJS Modules.

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() не покрыта никакими тестами.

Чтобы собирать покрытие кода при прогоне тестов, см. следующие сниппеты:

После прогона тестов вы получите отчёт, выглядящий примерно так:

✔ 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

Отчёт о покрытии даёт разбивку того, какая часть вашего кода покрыта тестами:

  • Покрытие строк (Line Coverage): процент строк, выполненных во время тестов.
  • Покрытие ветвей (Branch Coverage): процент ветвей кода (вроде инструкций if-else), протестированных.
  • Покрытие функций (Function Coverage): процент функций, которые были вызваны во время тестирования.

В этом примере:

  • 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% покрытия кода по всем метрикам.

Node.js предлагает два CLI-аргумента для управления включением или исключением конкретных файлов в отчёте о покрытии.

Флаг --test-coverage-include (coverageIncludeGlobs в API run()) ограничивает покрытие файлами, соответствующими заданному glob-шаблону. По умолчанию файлы в директории /node_modules/ исключены, но этот флаг позволяет явно их включить.

Флаг --test-coverage-exclude (coverageExcludeGlobs в API run()) исключает из отчёта о покрытии файлы, соответствующие заданному glob-шаблону.

Эти флаги можно использовать несколько раз, и когда оба используются вместе, файлы должны соответствовать правилам включения, при этом избегая правил исключения.

.
├── main.test.js
├── src
│   ├── age.js
│   └── name.js

src/age.js имеет далеко не оптимальное покрытие в отчёте выше, но с флагом --test-coverage-exclude (coverageExcludeGlobs в API run()) его можно полностью исключить из отчёта.

Наш тестовый файл тоже включён в этот отчёт о покрытии, но нам нужны только JavaScript-файлы в директории src/. В этом случае можно использовать флаг --test-coverage-include (coverageIncludeGlobs в API run()).

По умолчанию, когда все тесты проходят, Node.js завершается с кодом 0, что означает успешное выполнение. Однако отчёт о покрытии можно настроить так, чтобы завершаться с кодом 1, когда покрытие не проходит.

В настоящее время Node.js поддерживает пороги для всех трёх поддерживаемых видов покрытия:

Если бы вы захотели потребовать, чтобы в предыдущем примере покрытие строк было >= 90%, вы могли бы использовать флаг --test-coverage-lines=90 (lineCoverage: 90 в API run()).