Node.jsでのコードカバレッジの収集
Node.jsは、テストランナーを通じてコードカバレッジの組み込みサポートを提供しており、これは--experimental-test-coverageフラグを使用して有効にできます。
run() APIを使用している場合、coverageオプションをtrueに設定する必要があります。run() APIの詳細については、node:testのドキュメントを参照してください。
コードカバレッジとは?
コードカバレッジは、テストランナーのメトリクスであり、テスト中にプログラムのソースコードがどれだけ実行されたかを測定します。これにより、コードベースのどの部分がテストされ、どの部分がテストされていないかが明らかになり、テストスイートのギャップを特定するのに役立ちます。これにより、ソフトウェアのより包括的なテストが保証され、未検出のバグのリスクが最小限に抑えられます。通常はパーセンテージで表され、コードカバレッジのパーセンテージが高いほど、テストカバレッジがより徹底していることを示します。コードカバレッジの詳細な説明については、Wikipediaの「コードカバレッジ」の記事を参照してください。
基本的なカバレッジレポート
Node.jsでのコードカバレッジの仕組みを説明するために、簡単な例を見ていきましょう。
注: この例、およびこのファイル内の他のすべての例は、CommonJSを使用して書かれています。この概念に不慣れな場合は、CommonJSモジュールのドキュメントをお読みください。
function (, ) {
return + ;
}
function () {
return % 2 === 0;
}
function (, ) {
return * ;
}
. = { , , };
このモジュールには、add、isEven、multiplyという3つの関数があります。
テストファイルでは、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
カバレッジレポートは、コードのどの程度がテストでカバーされているかの内訳を提供します。
- 行カバレッジ (Line Coverage): テスト中に実行された行の割合です。
- 分岐カバレッジ (Branch Coverage): テストされたコードの分岐(if-else文など)の割合です。
- 関数カバレッジ (Function Coverage): テスト中に呼び出された関数の割合です。
この例では
main.jsは、multiply()関数がテストされていないため、行カバレッジが76.92%、関数カバレッジが66.67%と表示されます。カバーされていない行(9-11)がこの関数に対応します。main.test.jsはすべてのメトリクスで100%のカバレッジを示しており、テスト自体が完全に実行されたことを示しています。
含める、除外する
アプリケーションの開発中、特定のファイルやコード行を除外する必要がある状況に遭遇することがあります。
Node.jsは、特定のコードセクションを無視するためのコメントの使用や、パターン全体を除外するためのCLIなど、これを処理するためのメカニズムを提供します。
コメントの使用
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%のコードカバレッジを持つ同じレポートを生成します。
CLIの使用
Node.jsは、カバレッジレポートに特定のファイルを含めるか除外するかを管理するための2つの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は現在、サポートされている3つのカバレッジすべてに対して閾値をサポートしています。
- 行カバレッジ用の
--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