Javascript や TypeScript のプロジェクトで,単体テスト コードが実行された/実行されていないコードをソース コード エディター上に色分け表示(カバレッジ ハイライト)できれば,単体テストの消化効率をもっと上げられるのに,と思うことがあります。
本稿では,Javascript / TypeScript のエディターに Visual Studio Code (VS Code) を使い,コード カバレッジを VS Code 上で可視化する方法を解説します。
VS Code でカバレッジ ハイライトするには,Bradley Meck 氏 (Node.js コミッター) 制作の "Code Cover" を使用します。
Code Cover はシンプルな設定ファイル (coverageconfig.json) ひとつで動作する素晴らしい VS Code Extension ですが,日本語での紹介記事はもちろん,英語での紹介記事も見つからないのが現状です。本稿をきっかけに少しでも多くの開発者に知って(使って)いただければ幸いです。
TypeScript でカバレッジ ハイライトをサポートしたサンプル プロジェクトを GitHub のリポジトリに作成しました。Code Cover の動作確認にご利用ください。(Code Cover のインストールは後述のCode Cover のインストール)を参照)
†TypeScript, "tscodecover", https://github.com/kasecato/tscodecover.git
git clone https://github.com/kasecato/tscodecover.git
cd tscodecover
npm install
npm run build
code .
Javascript Babel ES2015 版のカバレッジ ハイライトは Bradley Meck 氏がサンプル プロジェクトを作成しています。
‡Javascript Babel ES2015, "example", https://github.com/bmeck/vscode-code-cover/tree/master/example
git clone https://github.com/bmeck/vscode-code-cover.git
cd vscode-code-cover/example/
npm install
npm run build
npm run coverage
code .
- VS Code のコマンド パレット を開きます Cmd-Shift-P (OSX) or Ctrl-Shift-P (Windows, Linux)
Extensions: Install Extension
を入力&選択しますext install codecover
を入力します。☁ マークを押下してインストールします- VS Code を再起動します。インストール完了メッセージが表示された後に,再起動を促すメッセージが表示されます
tscodecover/
├── .eslintrc.json
├── coverageconfig.json
├── package.json
├── tsconfig.json
|
├── .vscode/
| └── tasks.json
├── coverage/
| ├── coverage.json
| └── lcov.info
├── src/
│ └── Util/
│ └── NumberUtil.ts
├── test/
│ └── Util/
│ └── NumberUtil.test.ts
└── out/
ファイル | 役割 | 説明 |
---|---|---|
.eslintrc.json | ESLint 設定ファイル | TypeScript の文法を検証するルールを記載します |
coverageconfig.json | Code Cover 設定ファイル | coverage.json または lcov.info を指定します |
package.json | npm パッケージ管理ファイル | プロジェクトに必要なパッケージの依存関係やビルド スクリプトなどを設定します |
tsconfig.json | TypeScript 設定ファイル | トランスパイルする ES のバージョンや Sourcemap の出力を設定します |
tasks.json | VS Code タスク設定ファイル | VS Code のコマンド パレットから build や test 等のコマンドを使えるように登録します |
coverage.json | カバレッジ結果 | Code Cover が参照します |
lcov.info | カバレッジ結果 | Code Cover が参照します |
src | ソース コード | ソース コード TypeScript で記述します |
test | 単体テスト コード | テスト コードを TypeScript (mocha) で記述します |
out | 実行コード | TypeScript が Javascript にトランスパイル&出力されます |
{
"coverage": ["./coverage/lcov.info"],
"sourcemapped": ["./out/src/*/**.js"],
"automaticallyShow": true,
"ignore": ["*.json"]
}
設定項目 | 説明 |
---|---|
coverage | coverage.json または lcov.info を指定します |
sourcemapped | TypeScript の Sourcemap が出力されるフォルダを指定します |
automaticallyShow | true: 自動でカバレッジをハイライトします false: カバレッジをハイライトするには,ステータス バー Coverage をクリックするか,コマンド パレットから Highlight code coverage を実行する必要があります |
ignore | ハイライトしたくないファイルを指定することができます |
Code Cover はコード カバレッジの情報 coverage.json または lcov.info ファイルを解析して VS Code 上にカバレッジ ハイライトします。そのため,テスト コードのフレームワークと,カバレッジ出力をサポートするパッケージがそれぞれ必要となります。
パッケージ | 説明 |
---|---|
mocha | Javascript のテスト フレームワーク |
nyc | カバレッジ情報を含む coverage.json や lcov.info を出力します |
coverage.json や lcov.info が出力されるのであれば,どの Javascript テスト フレームワークでも問題ありません。つまり,TypeScript だけではなく Javascript のプロジェクトでも カバレッジ ハイライトが可能です。
今回は TypeScript を中心に解説しますが,Babel ES2015 (a.k.a. ES6) でも,同じ手順でカバレッジ ハイライトが可能です。
パッケージは直接コマンドでインストールできますが,依存関係を継続的に管理するためにも,package.json で管理します。一度書けば,30億のデバイスでどこでも実行できます。
{
"dependencies": {
},
"devDependencies": {
"@types/mocha": "^8.2.1",
"@types/node": "^14.14.34",
"eslint": "^7.22.0",
"mocha": "^8.3.2",
"nyc": "^15.1.0",
"typescript": "^4.2.3"
}
}
package.json に記述したパッケージのインストールは2つのワードで完了します。
npm install
mocha のテスト コードには大きく5種類の記述スタイル(インターフェイス)があります。今回は代表的な2種類を紹介します。
記述スタイル(インターフェイス) | メソッド |
---|---|
TDD (Test-Driven Development) | assert, describe(), context(), it(), before(), after(), beforeEach(), afterEach() |
BDD (Behavior-Driven Development) | should, suite(), test(), suiteSetup(), suiteTeardown(), setup(), teardown() |
- TDD とは
TDDは書いたコードをそのコードを使う側からの視点で考えるプログラミング手法です。この手法によって新たな設計が生まれます。 テストを書くことは、テスト対象のコードがどのように使われるのかを記述することで、そのテストに通るように対象のコードを書きます。 この手法はプログラマの専売特許で適切に使えばたくさんの利点があります。テストを書くことでドメインへの理解が深まり、命名がしやすくなります。 テストすることで理解のギャップが明確になりますし、自動化されたテストが一式あれば退行的な欠陥を見つけられます。 - Dan North
- BDD とは
BDDは開発の方法論です。プログラミング手法のTDDよりは、XPのような方法論に近いです。互いに視点の違う利害関係者を同じ土俵に乗せ、 同じものを評価し、同じ期待値を持つようにするための方法論です。利害関係者の価値観の相違に起因する問題がなければ、BDDを使う必要はないかもしれません。 - Dan North
Java の JUnit や C# の xUnit は標準では TDD スタイルのため,TDD 経験者は多いと思います。今回は開発視点で TDD スタイルを使用します。
export class NumberUtil {
public static isOdd(n: number): boolean {
if (n === undefined) {
return false;
} else if (n % 2 === 1) {
return true;
}
return false;
}
}
import * as assert from 'assert';
import {NumberUtil} from '../../src/util/NumberUtil';
suite('NumberUtil Tests', () => {
test('isOdd', () => {
// arrange
const n = 2501;
// act
const actual: boolean = NumberUtil.isOdd(n);
// assert
assert.equal(actual, true);
});
});
Arrange (テスト データの準備),Act(テスト対象の実行),Assert(検証) は AAA パターンと呼ばれ,単体テストを記述する一般的な方法です。AAA は MSDN で紹介されているため,AAA スタイルの単体テスト コードを記述しているエンジニアは C#er の可能性が高いです。
mocha と istanbul で coverage.json を出力します。
./node_modules/.bin/nyc --reporter=lcovonly -- ./node_modules/.bin/mocha --ui tdd ./out/test/**/*.js
引数 | 説明 |
---|---|
nyc | カバレッジを出力するコマンドです |
--reporter=lcovonly | nyc で lcov.info のみ出力させます |
mocha | テスト コードを実行するコマンドです。 |
--ui tdd | --ui tdd は TDD スタイルのテスト コードであることを意味します |
./out/test/**/*.js | 実行するテスト コード を指定します |
カバレッジを出力するだけのために,nyc の長大なコマンドを毎回思い出す必要はありません。package.json の scripts
項目にカバレッジを出力するコマンドを定義すれば,3ワードでカバレッジを取得できます。
{
"name": "tscodecover",
"version": "1.0.0",
"dependencies": {},
"devDependencies": {
"@types/mocha": "^8.0.0",
"@types/node": "^12.12.8",
"eslint": "^7.5.0",
"mocha": "^8.0.1",
"nyc": "^15.1.0",
"typescript": "^3.9.7"
},
"scripts": {
"clean": "rm -rf out",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"test": "mocha --ui tdd ./out/test/**/*.js",
"coverage": "nyc --reporter=lcovonly npm run test",
"build": "npm run compile",
"postbuild": "npm run coverage"
}
}
build
を実行するコマンドです。
npm run build
build
を実行すると,prebuild
,build
,postbuild
の順にコマンドが実行されます。「私がGulpとGruntを手放した理由」という記事で知りました。
カバレッジを取得するために,ターミナル(コマンド プロンプト)で毎回 npm run build
コマンドを実行するのはとても面倒です。
.vscode/tasks.json
にタスクを定義すると,VS Code のコマンド パレットに build
や test
と入力するだけで,package.json の スクリプトを VS Code 上で実行できます。素晴しいです。
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"type": "npm",
"script": "coverage",
"group": "test"
}
]
}
build
タスクを実行するショートカット キーは Cmd-Shift-B (OSX) or Ctrl-Shift-B (Windows, Linux)
です。コマンド パレットの場合は:
- VS Code のコマンド パレットを開く
build
と入力しTasks: Run Build Task
を選択
です。
ちなみに,test
タスクを実行するショートカット キーは Cmd-Shift-T (OSX) or Ctrl-Shift-T (Windows, Linux)
です。
TypeScript が VS Code 上でカバレッジ ハイライトできました。もちろんトランスパイルされる前の TypeScript がハイライトされます。
色 | 説明 |
---|---|
赤 return false; | 未実行コード (Line not covered) |
黄 if (n === undefined) { | 未実行分岐 (Branch not covered) |
無 return true; | 実行済コード (Covered) |
Code Cover の悪い点は,実行済コードがハイライトされないないことです。(TODO: 実行済みのコードがハイライトされるように Pull Request する 2016/02/05 プルリク完了)
- VS Code のカバレッジ ハイライトには Extension の Code Cover が必要であることがわかった
- VS Code に Extension の Code Cover がインストールできた
- Code Cover には coverage.json または lcov.info が必要であることがわかった
- coverage.json または lcov.info があれば,TypeScript でも Babel ES2015 でもカバレッジ ハイライトが可能であることがわかった
- mocha と istanbul を package.json でインストールできた
- mocha で TDD のテスト コードを書くことができた
- mocha と istanbul で coverage.json が取得できた
- VS Code にタスクを登録して,VS Code 上のコマンドだけでカバレッジが取得できるようになった
- TypeScript と VS Code でカバレッジ ハイライトができた
弊社の検収条件には,「単体テスト コードのコード カバレッジ 100% を満たすこと」と記載しておりますが,御社の納品物に単体テスト コードとカバレッジの成果物が含まれていないようです。
御社では TypeScript / Javascript のプロジェクトで「コード カバレッジ」を計測していますでしょうか?
速やかにご提出ください。
カバレッジの目的は何でしょうか?
コード網羅率(コードもうらりつ、英: Code coverage )コードカバレッジは、ソフトウェアテストで用いられる尺度の1つである。プログラムのソースコードがテストされた割合を意味する。(中略)
インフラなどの重要なアプリケーションでは、何らかの形でコード網羅率 100% のテストを実施したことを示すよう要求されることが多い。(中略)
このようなテスト作成の最終的な目的は、コードの修正時のバグ作りこみを防ぐ回帰テストの作成にある。 - Wikipedia
「テストカバレッジ(コードカバレッジ)の目標値はどれくらいがいいのか?」という質問とか、コードカバレッジの高さの自慢とかを、ときどき耳にする。でも、大事なポイントを外している。コードカバレッジは、コードのテストされていない部分を発見するための有用なツールである。ただテスト自体がどれだけ良いかという指標としては、テストカバレッジはほとんど役に立たない。(中略)
思慮深くテストを実施すれば、テストカバレッジはおそらく80%台後半か90%台になるだろう。100%は信用ならない。カバレッジの数字ばっかり気にして、自分が何をやっているかわかっていない人間のいる臭いがする。- マーティン・ファウラー
網羅率を目標にするのは馬鹿げています。最もリスクが大きいコードを10%網羅することは、リスクのないコードを99%網羅することよりも遥かに優れています。テストの網羅率よりもリスクの網羅率のほうが優れた指標だと思います。私はAttribute Component Capability Matrixを使ってリスクを評価し何をどのように網羅するかを決めています(James WhittakerのHow Google Tests Softwareを参照ください)。 - ゴイコ・アジッチ
コード カバレッジが 100% のプロジェクトの品質が高いのでしょうか?
No です。
カバレジが多くなったときに欠陥が減る結果になることを示すエビデンスはほとんどないのが実情です - ナチーアパン・ナガパン,トーマス・ボール 「エビデンスに基づくエラー予測」
カバレジは実行される命令の割合だけであり,命令の間違いを含んでいるかを考慮しない
テスト実施者のデータで命令や分岐が実行されても,顧客や利用時のシナリオとデータが異なる
Windows Vista 4千万行で計測したところ,コードカバレジと品質との間には弱い正の相関があり,精度 83.3%,再現率 54.8% 低い値であった
Microsoft の ナチーアパン・ナガパン,トーマス・ボールは論文「エビデンスに基づくエラー予測」で提案しています。 コードの変更量が多いコードや複雑度が高いコードのカバレッジを高めに設定する
プロジェクトでカバレッジ一律 100% を求められたら,必ず関係者と納得がいくまで議論してみましょう。
VS Code は "Extension"(機能拡張)がサポートされています。Go 言語,Python,Unity,PHP や Cordova(Android や iOS のデバイス上で)のデバッグ機能,eslint,tslint や hlint などの lint 機能,もちろん Vim スタイルのサポートも追加が可能です。「VS Code は環境」です。VS Code 拡張機能の一覧は Visual Studio Marketplace (Preview) で確認できます。
VS Code にも有償のテスト ツールがあります。Artem Govorov 氏が制作した "Wallaby.js for Visual Studio Code" が高機能で, コードの修正で即時にテストの実行結果や実行/未実行を VS Code 上にカバレッジ ハイライトする機能があります。
- 何言語のカバレッジを取得していますか?
- カバレッジを取得するフレームワークは何ですか?
- IDE や エディターは何を使用していますか?
- カバレッジ ハイライトは使っていますか?
- カバレッジは何%を目標にしますか?
- カバレッジを何の指標に使っていますか?
- カバレッジを取得する理由は何ですか?
- テストコードは TDD ですか? BDD ですか? それ以外ですか?
- TDD や BDD を選択した理由は何ですか?
- カバレッジで CI をしていますか?
- たまに本記事が見られているようなので,
istanbul
をnyc
に移行しました
This module is no longer maintained, try this instead: npm i nyc Visit https://istanbul.js.org/integrations for other alternatives.
- Wikipedia, 「コード網羅率」, https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%BC%E3%83%89%E7%B6%B2%E7%BE%85%E7%8E%87
- Martin Fowler's Bliki (ja), 「テストカバレッジ」, http://bliki-ja.github.io/TestCoverage/
- InfoQ, 「バーチャルパネル: コードとテストの比率、TDD、BDD」, http://www.infoq.com/jp/articles/virtual-panel-tdd-bdd
- Monaca X Onsen Blog, "Mocha + Chai.js unit testing for ES6 with Istanbul code coverage", https://onsen.io/blog/mocha-chaijs-unit-test-coverage-es6/
- @IT Insider.NET, 「Visual Studio Codeでエクステンション機能を使ってみよう」, http://www.atmarkit.co.jp/ait/articles/1511/27/news029.html
- O'Reilly Japan, 『Making Software――エビデンスが変えるソフトウェア開発』, https://www.oreilly.co.jp/books/9784873115115/
- Qiita, 「[意訳]私がGulpとGruntを手放した理由」, http://qiita.com/chuck0523/items/dafdbd19c12efd40e2de
- GitHub, "Roadmap: TSLint -> ESLint #4534", palantir/tslint#4534
- istanbul, "Tools That Integrate With Istanbul API's", https://istanbul.js.org/integrations/