Karmaを使うことでフロントエンドの単体テストを楽しくかつ機能的に行うことができます。
ステップを通して少しづつ理解できるように執筆しました。
また、最後に重要だろうと思われる補足内容を記載しました。
Karmaの導入に一役買えれば幸いです。
テスト全体の流れについては以前書いた記事フロントエンドにテストを導入を参照してください。
Karmaとは
ブラウザ上で 単体テストを実行するためのテストランナーです。
テストを実行するだけでなくファイルの変更監視や結果のレポートを出力してくれたりと単体テストに必要な機能が一通りそろっています。
特定のフレームワームに依存しておらず汎用的に使えるツールで、プラグインを使った機能の拡張が強力です。
前提
Nodejs,npm,chromeが導入済みであること
流れ
Karmaは4つの大きな機能(プラグイン)が存在します。
Step 3 〜 Step 6でそれぞれ解説していきます。
- Framework
- Launcher
- Preprocessor
- Reporter
※Stepのリンク先はソースです。
Step | 表題 | 目的 |
---|---|---|
Step 1 | 準備 | プロジェクトの作成/karmaのインストール |
Step 2 | ログレベルを変更 | Karmaの設定方法の学習 |
Step 3 | Framework, ファイルの登録 | テストライブラリやテスト対象ファイルやテストファイルの登録 |
Step 4 | Launcherを登録 | テストを実行するブラウザを登録 |
Step 5 | Preprocessorを登録 | ファイル読み込みの前に処理を割りこませる |
Step 6 | Reporterを登録 | テスト結果に対して処理を行う |
ゴール
Karmaの全体の流れと設定ファイルの書き方を理解する
Step 1 準備
package.jsonの作成
以下のコマンドから作成します。返答はエンターでOKです。
npm init
karmaとkarma-cliのインストール
karma-cliをインストールすることでkarma
とうつだけでプロジェクト内のkarmaを実行してくれます。
npm install --save-dev karma
npm install -g karma-cli
Karmaが動作しているか確認
この時点でkarma自体は実行できます。起動してみましょう。
karma start
09 05 2016 12:23:14.712:WARN [karma]: No captured browser, open http://localhost:9876/
09 05 2016 12:23:14.721:INFO [karma]: Karma v0.13.22 server started at http://localhost:9876/
補足されたブラウザがないよ。と怒られてますね。
ですがブラウザで記載されたURLにアクセスすることは可能です。
以下の様な画面が表示されます。
karma start
はWebサーバを立てることと言ってもいいかもしれません。
そのWebサーバーにプラグインで機能を追加していくイメージです。
またStep6で出てきますがKarmaがコンソールに結果を出力する際にreportersにデフォルトのprogress
が使われています。
Step 2 ログレベルを変更
KarmaのデフォルトのログレベルはINFOです。
学習時はDEBUGのほうが何かと都合がいいのでDEBUGに変更しましょう。
方法は2通りあります。
その1 実行時のオプションで指定
--log-level
で指定可能です。
karma start --log-level debug
09 05 2016 12:31:49.435:DEBUG [plugin]: Loading karma-* from /Users/howdy/VscodeProjects/study-karma/node_modules
09 05 2016 12:31:49.464:DEBUG [karma]: List of files has changed, trying to execute
09 05 2016 12:31:49.465:WARN [karma]: No captured browser, open http://localhost:9876/
09 05 2016 12:31:49.470:INFO [karma]: Karma v0.13.22 server started at http://localhost:9876/
DEBUG行が追加されていますね。
その2 設定ファイル(karma.conf.js)で指定
Karmaの設定はkarma.conf.js
がデフォルトで読みこまれます。
karma init
を実行して上記ファイルを簡単に作れますが、学習のため手動で作っていきましょう。
touch karma.conf.js
module.exports = function(config) {
config.set({
logLevel: config.LOG_DEBUG
})
}
karma start
09 05 2016 12:38:32.036:DEBUG [plugin]: Loading karma-* from /Users/howdy/VscodeProjects/study-karma/node_modules
09 05 2016 12:38:32.068:DEBUG [karma]: List of files has changed, trying to execute
09 05 2016 12:38:32.069:WARN [karma]: No captured browser, open http://localhost:9876/
09 05 2016 12:38:32.074:INFO [karma]: Karma v0.13.22 server started at http://localhost:9876/
どっちを使うべき?
特に理由がなければ設定ファイルを使いましょう。
設定ファイルで指定が可能でコマンドで指定できない項目がありますし、そもそも打つのが大変です。
コマンドオプションで使うケースとしては一時的に設定を変更したい時とかでしょうか。(コマンドオプションの方が設定ファイルより優先度が高いです)
以降は全て設定ファイルで記載していきます。
Tips 設定ファイルについて
設定ファイルはKarmaによってrequireされます。
※require('./karma.conf.js')が内部的に呼ばれている形です。
そのためmodule.exports
という形になっているわけですね。
また引数のconfigにはデフォルトの設定が入っています。
config.set(...)
でデフォルト設定を上書きしていくイメージです。
Step 3 Framework, ファイルの登録
Karmaにファイルを登録する方法について記載します。
登録することでKarma起動時に読み込まれます。
htmlでいう<script>
で読み込むのと同じイメージです。
テスト対象ファイルの作成と登録
mkdir app && touch app/app.js
中身はコンソールへのログ出力と、add,subtract関数を定義しておきます。
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
設定ファイルのfiles
に上記ファイルを追加します
module.exports = function(config) {
config.set({
+ files: [
+ 'app/app.js'
+ ],
logLevel: config.LOG_DEBUG
})
}
karma start
...
09 05 2016 21:46:52.236:DEBUG [watcher]: Watching "/Users/howdy/VscodeProjects/study-karma/app/app.js"
...
[watcher]行が増えていることが確認できます。
Jasmineのインストール
テストフレームワークであるJasmineをkarmaで使うためにkarma-jasmineをインストールします。
npm install --save-dev karma-jasmine
設定ファイルのframeworks
にjasmineを追加します
module.exports = function(config) {
config.set({
+ frameworks: ['jasmine'],
files: [
'app/app.js'
],
logLevel: config.LOG_DEBUG
})
}
テストファイルの作成と登録
mkdir test && touch test/appSpec.js
describe('add関数のテスト', function() {
it('1 + 2 は 3', function() {
expect(add(1, 2)).toBe(3);
});
});
設定ファイルのfiles
に追加します。
*
をつかってパターンマッチで指定することが可能です。
※正確にはglobスタイルです。
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
+ 'test/*Spec.js'
],
logLevel: config.LOG_DEBUG
})
}
Tips パターンマッチの読み込まれる順番
パターンマッチで複数登録が可能ですが、その場合に読み込まれるのはアルファベット順になります。
Karmaを実行してブラウザでアクセス
karma start
ブラウザでアクセスしてみましょう。
Chrome 50.0.2661 (Mac OS X 10.11.2): Executed 1 of 1 SUCCESS (0.006 secs / 0.001 secs)
アクセスした瞬間に処理が流れます。
以下の様な流れです。
ブラウザでアクセス→登録したjsファイルが読み込まれる→テストが実行される
Tips 外部ライブラリを使っている場合
アプリケーションのjs内でMoment.jsなどの外部ライブラリを使っているケースもあるかと思います。
この場合もfilesに登録してあげればOKです。
※filesに記載した順番に読み込まれるので必要になるjsより先に書く形になります。
(つまりアプリケーションのhtmlで記載しているscriptタグと同じ順です)
Step 4 Launcherを登録
ブラウザから手動でアクセスするのは面倒です。
これを自動化するための仕組みがランチャーです。
ランチャーを使うことでkarma start
時に自動でテストを実行するURLを開いたブラウザが立ち上がります。
npm install --save-dev karma-chrome-launcher
設定ファイルのbrowsers
にChromeを追加します
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
'test/*Spec.js'
],
+ browsers: ['Chrome'],
logLevel: config.LOG_DEBUG
})
}
karma start
ブラウザが自動で起動しましたね。
browsersとなっているように複数指定することが可能です。
つまり複数ブラウザを同時にテストすることもできます。
Tips ファイル監視による再実行
テスト対象ファイルでもテストファイルでもいいので書き換えてみましょう。
設定ファイルには記載していませんがautoWatch
の設定がデフォルトでtrueになっており、ファイルの変更を監視しています。
そのため自動でテストが再実行されます。
Step 5 Preprocessorを登録
今まではブラウザでそのまま動作する素のJavascriptファイルを登録していました。
CoffeeScriptやTypeScriptやBabelを使用している場合は当然そのままでは動きません。
そのため Karmaに読み込ませる前に変換する必要があります。
このファイルの読み込みの前に処理を行うのが プリプロセッサです。
ここではテストファイルをCoffeeScriptにしてみます。
karma-coffee-preprocesserのインストール
CoffeeScriptをKarmaで使うためにkarma-coffee-preprocessorをインストールします
npm install --save-dev karma-coffee-preprocessor
テストファイルをCoffeeScriptで作成する
cp test/appSpec.js test/appSpec.coffee
describe('add関数のテスト', () ->
it('1 + 2 は 3', () ->
expect(add(1, 2)).toBe(3);
);
);
設定ファイルのpreprocessors
にcoffeeを追加します。
また.jsから.coffeeの方を読み込むようにします。
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
- 'test/*Spec.js'
+ 'test/*Spec.coffee'
],
+ preprocessors: {
+ 'test/*Spec.coffee': ['coffee']
+ },
browsers: ['Chrome'],
logLevel: config.LOG_DEBUG
})
}
karma start
テストファイルをCoffeeScriptで書くことができました。
karma-coffee-preprocessorのオプションを設定
この状態だとjsファイルに変換した後の状態になっているためエラーが起きた時にCoffeeScriptのどこで起きたのかわかりません。
coffee-scriptモジュールにはソースマップを出力する機能があるためそれを利用するように教えてあげる必要があります。
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
'test/*Spec.coffee'
],
preprocessors: {
'test/*Spec.coffee': ['coffee']
},
+ coffeePreprocessor: {
+ options: {
+ sourceMap: true
+ }
+ },
browsers: ['Chrome'],
logLevel: config.LOG_DEBUG
})
}
試しにこの状態でテストをわざと間違えてエラーにしてあげましょう。
すると以下の様にcoffeeのどこでエラーになったかが正確にわかります。
Chrome 50.0.2661 (Mac OS X 10.11.2) add関数のテスト 1 + 2 は 3 FAILED
Expected 3 to be 111111.
at Object.<anonymous> (/Users/howdy/VscodeProjects/study-karma/test/appSpec.js:3:30 <- appSpec.coffee:3:26)
Tips CoffeeScript以外はどうする?
TypeScriptやBabelも同様にkarma-xxx-preprocessor
が提供されているのでそれらを使えばOKです。
WebpackやBrowserifyは目的は違いますが 事前に変換するという意味では同じため同様にpreprocessorを使用します。
それぞれ使うプリプロセッサによってオプションが違いますので各々のサイトで確認しましょう。
Step 6 Reporterを登録
レポーターを利用することでテスト結果をカスタマイズすることができます。
ここでは代表的な3つを紹介します。
karma-mocha-reporter
karma-mocha-reporterはテスト結果を見やすく表示してくれます。
npm install --save-dev karma-mocha-reporter
設定ファイルのreporters
にmochaを追加します
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
'test/*Spec.coffee'
],
preprocessors: {
'test/*Spec.coffee': ['coffee']
},
coffeePreprocessor: {
options: {
sourceMap: true
}
},
browsers: ['Chrome'],
+ reporters: ['mocha'],
logLevel: config.LOG_DEBUG
})
}
karma start
karma-coverage
karma-coverageはテストのカバレッジをhtmlで出力することができます。
npm install --save-dev karma-coverage
設定ファイルのreporters
にcoverageを追加します。
coverage対象を定めるためにpreprocessors
に対象のjsファイルを追加します。
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
'test/*Spec.coffee'
],
preprocessors: {
+ 'app/app.js': ['coverage'],
'test/*Spec.coffee': ['coffee']
},
coffeePreprocessor: {
options: {
sourceMap: true
}
},
browsers: ['Chrome'],
- reporters: ['mocha'],
+ reporters: ['mocha', 'coverage'],
logLevel: config.LOG_DEBUG
})
}
karma start
プロジェクトフォルダ直下にcoverage
ディレクトリが作成されます。
coverage
└── Chrome\ 50.0.2661\ (Mac\ OS\ X\ 10.11.2)
├── app
│ ├── app.js.html
│ └── index.html
├── base.css
├── index.html
├── prettify.css
├── prettify.js
├── sort-arrow-sprite.png
└── sorter.js
2 directories, 8 files
app.js.htmlをブラウザで開いてみましょう。
subtract関数のテストを行っていないため赤くなりパーセンテージが下がってます。
karma-junit-reporter
karma-junit-reporterはJUnitXML形式でテスト結果を出力できます。
npm install --save-dev karma-junit-reporter
設定ファイルのreporters
にjunitを追加します。
junitReporter.outputDirに出力フォルダを指定します。(指定しない場合、プロジェクト直下にできて邪魔になります)
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
'test/*Spec.coffee'
],
preprocessors: {
'app/app.js': ['coverage'],
'test/*Spec.coffee': ['coffee']
},
coffeePreprocessor: {
options: {
sourceMap: true
}
},
browsers: ['Chrome'],
- reporters: ['mocha', 'coverage'],
+ reporters: ['mocha', 'coverage', 'junit'],
+ junitReporter: {
+ outputDir: 'report'
+ },
logLevel: config.LOG_DEBUG
})
}
karma start
report
└── TESTS-Chrome_50.0.2661_(Mac_OS_X_10.11.2).xml
0 directories, 1 file
その他 Karmaの実行時オプション
前項までに扱っていた内容ですとkarma start
しか出てきませんでしたが
他にもkarma init
とkarma run
という2つのコマンドがあります。
それぞれ解説します。
karma init
karma.conf.jsを対話形式で作るためのオプションです。
本記事では学習のために敢えて使いませんでした。
一度自分なりの設定ファイルを作ってしまったらそれをコピったほうが早いので気がします。
karma run
テストだけを実行するコマンドです。
karma start
でKarmaのサーバーを立てておきます。(ブラウザもそのまま起動したままにしておく)
その状態で 別のコンソールからkarma run
を実行することでテストを実行することが可能です。
ソースファイルを変更するとテストが再実行されますが、このときに裏でkarma run
が呼ばれているのだと思われます。
karma start
主にやっていることは以下の3点です。
- KarmaのWebサーバーを立てる
- テスト実行用ブラウザーを起動する
-
karma run
を一度だけ呼ぶ
Tips 設定ファイルの指定
init, start, runの全てでファイル名を指定することが可能です。
karma start karma-dev.conf.js
のように書けます。
また本記事ではkarma.conf.jsしか扱いませんでしたが.coffeeでも記述可能です。
指定しない場合は以下の4ファイルを読みこむようになっています。
OSSのプロジェクトでkarma.conf.jsが見つからないときは.configにおいてあるかもしれません。
- ./karma.conf.js
- ./karma.conf.coffee
- ./.config/karma.conf.js
- ./.config/karma.conf.coffee
Tips --single-run オプション
--single-run
オプションを指定することでテストを一度だけ実行することが可能です。
karmaを動かした後にビルドする際に使用します。
karma start --single-run
その他 設定ファイルの項目
Stepごとの手順では省略しましたが重要そうなものを記載しておきます。
詳細はリファレンスを参照してください。
Tips 項目の種類は2種類ある
Karmaが用意している項目とプラグインが独自に定義した項目の2種類があるのに注意してください。
ここではKarmaが用意している項目しか扱いませんがStepの中で出てきたcoffeePreprocessor
はkarma-coffee-preprocessor
が独自に定義した項目です。
そのためKarmaのリファレンスには記載されていません。
basepath
filesで読み込むファイルのベースを指定できます。
デフォルトは''
です。
karma.conf.jsをプロジェクト直下から変更した場合はいじったほうが記載が楽になります。
exclude
filesで登録したファイルから除外したいファイルを指定できます。
※files同様*
を使った指定が可能です。
plugins
自分で書くことはないかもしれませんが、仕組み上重要なので記載しておきます。
npmで各種プラグインをインストールした後にkarma.conf.js
にjasmine
やChrome
と書くだけでなぜ使えるのかですが、このpluginsの設定のおかげです。
デフォルトでkarma-*
が指定されているためkarma-*
でマッチするnode_moduleを自動で読み込んでいるわけですね。
試しにplugins:[]
と記載して実行すると以下のように見つからない旨のエラーになります。
...
Error: No provider for "framework:jasmine"! (Resolving: framework:jasmine)
...
autoWatch
デフォルトはtrue
です。
filesで指定したファイルを監視して変更があったら再実行してくれます。
autoWatchBatchDelay
デフォルトは250
です。
監視対象ファイルが変更されてから何msテストを実行するまで待機するかどうかの設定です。
例えば5000を指定した場合、5秒間の間にファイル保存を何度やってもテストは5秒後に1回しか走りません。
port
デフォルトは9876
です。
singleRun
デフォルトはfalse
です。
CI用のモードです。
true
を指定した場合、テスト実行後にサーバーやブラウザを落とします。
client.args
デフォルトはundefined
です。
karma-jasmineやkarma-mochaなどにオプションを渡すためのプロパティです。
例えばgrepを使えばテスト項目を絞ることができます。
config.set({
...
client: {
args: ['--grep', 'add関数のテスト']
}
})
karma run
で指定することも可能です。以下のように--
の後に指定します。
karma run -- --grep='add関数のテスト'
Tips オプションについて
例えばjasmineはspecFilter
という機能を使ってテスト対象を絞り込むことが可能です。
grepを指定した場合、karma-jasmine
がgrepの内容をjasmine上のspecFilterに登録してくれています。
その他 プラグインの探し方
npmjsで検索になるのでしょうか。
良いやり方がありましたら教えて下さい。
あとがき
Karma自体はそんなに内容は深くないので問題はないのですがプラグインに渡すプロパティの設定はちょっと厄介かもしれません。
プラグインのドキュメントが整備されていないとソースを見ないとちょっとわからないかもです。。