karma + babel + istanbul でフロントエンドのカバレッジを出す設定 2016年版


この記事は [CureApp Advent Calendar][2016/cureapp] 23日目の記事です。

今日はフロントエンドのカバレッジの話です。

# テストカバレッジとは

テストカバレッジとは、テストの際にどれくらいの割合のコードがテストされているか・されていないかを集計した情報のことです。

原理としては、JavaScript の場合は、テスト実行前にスクリプトに変換をかけて、各行に、 **その行を実行したことを記録するコード** を追加し(この変換のことを Instrumentation と言います)、その状態でテストを走らせます。全テストケース終了後に、各行から出力された行の実行情報を集計してまとめることで、カバレッジレポートを作成しています。

# テストカバレッジの意味

テストカバレッジが出せるようになると、行ごとにテストされた・されてないという情報を知ることができ、全体で何%がテストされたという情報をトラッキングすることが出来ます。特にテストされていない割合が重要で、どういうモジュール・ファイルに非カバレッジ行が多いのかを知ることで、どいういうテストが足りていないとか、どの辺のモジュールでバグが出るリスクが高いなどの知見を得ることが出来ます。また、今後どういうテストに力を入れるべきなのかという判断の材料になります。

# フロントエンドのカバレッジ

さて、ここからはフロントエンド JS でカバレッジを出すやり方の話です。

去年似たような記事で、[karma + babel + browserify + isparta を使って、es2015 で書いたフロントエンドテストのカバレッジを出す設定][2015] を書きましたが、正直設定項目が多く、記事を書きながらもこれは辛いと感じていました。また、es2015 のカバレッジ変換ライブラリの [isparta][isparta] が既にメンテナンス放棄されていて、先行きにも不安がありました。

そんな中、今年中盤に [istanbul][istanbul] が [babel][babel] ベースに書き直されるということがあり([babel-plugin-istanbul][babel-plugin-istanbul] というプラグインができました)、変換を babel に一本化出来るようになり、現在はかなりスッキリ設定できるようになってきています。以下その設定の仕方を説明していきます。

# :wrench: 設定

**前提**: Node.js が install されて、package.json がすでにあるものとします。

karma-cli を持っていない場合は、まず karma-cli をグローバルインストールします。

    npm install -g karma-cli

次に karma をローカルインストールします。

    npm install karma
    
次に `karma init` コマンドで karma.conf.js を作成します。

    karma init

色々と質問を聞かれるのでプロジェクトの事情に合わせて適切なものを選んでいきます。コマンドが終わると、karma.conf.js が生成されます。

次に、必要な依存モジュールをインストールします。ここでは、例として、browserify で設定していきます。webpack を使う場合は適宜読み替えてください。

    npm install karma-browserify karma-coverage babelify babel-preset-es2016 babel-plugin-istanbul --save-dev
    
それぞれ、どういうモジュールか説明すると以下のようになります。

- karma-browserify
	- karma で browserify を使うためのモジュール
- karma-coverage
	- karma で coverage レポートを出すためのモジュール、instrumentation する機能も持っているが、browserify / babel が絡むと機能しないので、ここでは、レポート作成機能のみ使う
- babelify
	- browserify をしながら babel してくれるツール
- babel-preset-es2016
	- es2016 の変換セット
- **babel-plugin-istanbul**
	- coverage データの埋め込みをしてくれる babel プラグイン

上のモジュールがインストールできたら、次に、`karma.conf.js` に下のような設定を追加します。

```js:karam.conf.js
module.exports = function(config) {
  config.set({
    ...,
    frameworks: ['mocha', 'browserify'],
    files: [
      'test/*.js'
    ],
    preprocessors: {
      'test/*.js': 'browserify'
    },
    browserify: {
      debug: true,
      transform: [
        ['babelify', { presets: ['es2016'], plugins: ['istanbul'] }]
      ]
    },
    reporters: ['progress', 'coverage'],
    coverageReporter: { type: 'lcov' },
    ...,
  })
}
```

`framework` で browserify を設定すること、`preprocessor` で テスト対象に browserify を設定することなどは、一般的な設定と同じです。

ポイントは以下の browserify と babelify のパラメータを入れている部分です。

```
    browserify: {
      debug: true,
      transform: [
        ['babelify', { presets: ['es2016'], plugins: ['istanbul'] }]
      ]
    },
```

ここで babel プラグインとして、istanbul を指定することで、ソースコードにカバレッジ出力用のコードが付加されます。

最後に、reporter で coverage を指定して、coverageReporter type として `lcov` を指定しています。

以上を設定した上で `karma start` コマンドを打ってテストを実行します。

```
$ karma start
20 12 2016 21:04:34.226:INFO [framework.browserify]: bundle built
20 12 2016 21:04:34.236:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
20 12 2016 21:04:34.237:INFO [launcher]: Launching browser Chrome with unlimited concurrency
20 12 2016 21:04:34.244:INFO [launcher]: Starting browser Chrome
20 12 2016 21:04:35.694:INFO [Chrome 57.0.2950 (Mac OS X 10.12.0)]: Connected on socket /#2KRfyyHsASYaf00QAAAA with id 23867396
Chrome 57.0.2950 (Mac OS X 10.12.0): Executed 4 of 4 SUCCESS (0.016 secs / 0.001 secs)
```

上のように、コマンドが成功すると、`coverage/[ブラウザバージョン]/lcov-report/index.html` というファイルに、カバレッジレポートが作成されます。ブラウザで開くと下のような画面が表示されるはずです。

ファイルインデックス

![スクリーンショット 2016-12-20 21.13.35.png](https://qiita-image-store.s3.amazonaws.com/0/13825/16ee83c1-d0d6-ae04-a253-e62beb4f5f71.png "スクリーンショット 2016-12-20 21.13.35.png")

各ファイルレポート

![スクリーンショット 2016-12-20 21.13.48.png](https://qiita-image-store.s3.amazonaws.com/0/13825/81146174-66ee-fcaf-0fc9-d637b2acc2bd.png "スクリーンショット 2016-12-20 21.13.48.png")


## :octocat: デモレポジトリ

例として上の設定をしたサンプルレポジトリを下に作成しました。

- [サンプルレポジトリ][sample-repo]
- [サンプルレポート][sample-doc]

# :green_apple: まとめ

さて、去年までは、[isparta][isparta] や [browserify-istanbul][browserify-istanbul] などの、よく分からない黒魔術的モジュールに依存してしまっていた karma 環境でのカバレッジレポートですが、[babel-plugin-istanbul][babel-plugin-istanbul] が出来たおかげでかなりスッキリと設定が書けるようになりました。

個人的に、フロントエンドでカバレッジをきちんと出している人を見ることは割と稀ですが、これを機にカバレッジレポートを見ながら心の余裕が持てる開発を始めてみるのはいかがでしょうか :sunglasses: 

Happy testing with coverage report!

明日は @janus_wel さんの話です!

[2016/cureapp]: http://qiita.com/advent-calendar/2016/CureApp
[2015]: http://qiita.com/kt3k@github/items/df783ae54caa4eeca2ae
[babel]: https://babeljs.io/
[istanbul]: https://github.com/gotwarlost/istanbul
[babel-plugin-istanbul]: https://github.com/istanbuljs/babel-plugin-istanbul
[sample-repo]: https://github.com/kt3k/example-karma-browserify-babel-istanbul
[sample-doc]: https://kt3k.github.io/example-karma-browserify-babel-istanbul/coverage/Chrome%2057.0.2950%20(Mac%20OS%20X%2010.12.0)/lcov-report/
[isparta]: https://github.com/douglasduteil/isparta
[browserify-istanbul]: https://github.com/devongovett/browserify-istanbul