Edited at

step by stepで始めるKarma

More than 1 year has passed since last update.

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の全体の流れと設定ファイルの書き方を理解する

ステップバイステップで始めるKarma ゴール.png


Step 1 準備


package.jsonの作成

以下のコマンドから作成します。返答はエンターでOKです。


package.jsonの作成

npm init



karmaとkarma-cliのインストール

karma-cliをインストールすることでkarmaとうつだけでプロジェクト内のkarmaを実行してくれます。


karmaのインストール

npm install --save-dev karma

npm install -g karma-cli


Karmaが動作しているか確認

この時点で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にアクセスすることは可能です。

以下の様な画面が表示されます。

Screen Shot 2016-05-12 at 9.43.36 PM.png

karma startはWebサーバを立てることと言ってもいいかもしれません。

そのWebサーバーにプラグインで機能を追加していくイメージです。

またStep6で出てきますがKarmaがコンソールに結果を出力する際にreportersにデフォルトのprogressが使われています。

ステップバイステップで始めるKarma Step1.png


Step 2 ログレベルを変更

KarmaのデフォルトのログレベルはINFOです。

学習時はDEBUGのほうが何かと都合がいいのでDEBUGに変更しましょう。

方法は2通りあります。


その1 実行時のオプションで指定

--log-levelで指定可能です。


debugレベルを指定

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を実行して上記ファイルを簡単に作れますが、学習のため手動で作っていきましょう。


karmaの設定ファイルを作成

touch karma.conf.js



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(...)でデフォルト設定を上書きしていくイメージです。

ステップバイステップで始めるKarma Step2.png


Step 3 Framework, ファイルの登録

Karmaにファイルを登録する方法について記載します。

登録することでKarma起動時に読み込まれます。

htmlでいう<script>で読み込むのと同じイメージです。


テスト対象ファイルの作成と登録


テスト対象ファイルを作成

mkdir app && touch app/app.js


中身はコンソールへのログ出力と、add,subtract関数を定義しておきます。


app.js

function add(a, b) {

return a + b;
}

function subtract(a, b) {
return a - b;
}


設定ファイルのfilesに上記ファイルを追加します


karma.conf.js

module.exports = function(config) {

config.set({
+ files: [
+ 'app/app.js'
+ ],
logLevel: config.LOG_DEBUG
})
}


karmaを実行

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をインストールします。


karma-jasmineのインストール

npm install --save-dev karma-jasmine


設定ファイルのframeworksにjasmineを追加します


karma.conf.js

module.exports = function(config) {

config.set({
+ frameworks: ['jasmine'],
files: [
'app/app.js'
],
logLevel: config.LOG_DEBUG
})
}


テストファイルの作成と登録


テストファイルを作成

mkdir test && touch test/appSpec.js



appSpec.js

describe('add関数のテスト', function() {

it('1 + 2 は 3', function() {
expect(add(1, 2)).toBe(3);
});
});

設定ファイルのfilesに追加します。

*をつかってパターンマッチで指定することが可能です。

※正確にはglobスタイルです。


karma.conf.js

module.exports = function(config) {

config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
+ 'test/*Spec.js'
],
logLevel: config.LOG_DEBUG
})
}


Tips パターンマッチの読み込まれる順番

パターンマッチで複数登録が可能ですが、その場合に読み込まれるのはアルファベット順になります。


Karmaを実行してブラウザでアクセス


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タグと同じ順です)

ステップバイステップで始めるKarma Step3.png


Step 4 Launcherを登録

ブラウザから手動でアクセスするのは面倒です。

これを自動化するための仕組みがランチャーです。

ランチャーを使うことでkarma start時に自動でテストを実行するURLを開いたブラウザが立ち上がります。


chromeランチャーをインストール

npm install --save-dev karma-chrome-launcher


設定ファイルのbrowsersにChromeを追加します


karma.conf.js

module.exports = function(config) {

config.set({
frameworks: ['jasmine'],
files: [
'app/app.js',
'test/*Spec.js'
],
+ browsers: ['Chrome'],
logLevel: config.LOG_DEBUG
})
}


karmaを実行

karma start


ブラウザが自動で起動しましたね。

browsersとなっているように複数指定することが可能です。

つまり複数ブラウザを同時にテストすることもできます。


Tips ファイル監視による再実行

テスト対象ファイルでもテストファイルでもいいので書き換えてみましょう。

設定ファイルには記載していませんがautoWatchの設定がデフォルトでtrueになっており、ファイルの変更を監視しています。

そのため自動でテストが再実行されます。

ステップバイステップで始めるKarma Step4.png


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



appSpec.coffee

describe('add関数のテスト', () ->

it('1 + 2 は 3', () ->
expect(add(1, 2)).toBe(3);
);
);

設定ファイルのpreprocessorsにcoffeeを追加します。

また.jsから.coffeeの方を読み込むようにします。


karma.conf.js

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を実行

karma start


テストファイルをCoffeeScriptで書くことができました。


karma-coffee-preprocessorのオプションを設定

この状態だとjsファイルに変換した後の状態になっているためエラーが起きた時にCoffeeScriptのどこで起きたのかわかりません。

coffee-scriptモジュールにはソースマップを出力する機能があるためそれを利用するように教えてあげる必要があります。


karma.conf.js

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を使用します。

それぞれ使うプリプロセッサによってオプションが違いますので各々のサイトで確認しましょう。

ステップバイステップで始めるKarma Step5.png


Step 6 Reporterを登録

レポーターを利用することでテスト結果をカスタマイズすることができます。

ここでは代表的な3つを紹介します。


karma-mocha-reporter

karma-mocha-reporterはテスト結果を見やすく表示してくれます。


karma-mocha-reporterをインストール

npm install --save-dev karma-mocha-reporter


設定ファイルのreportersにmochaを追加します


karma.conf.js

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を実行

karma start


こっちのほうが私は好きですね。

Screen Shot 2016-05-12 at 10.26.53 PM.png

比較用に標準(変更前)も貼っときます。

Screen Shot 2016-05-12 at 10.28.05 PM.png


karma-coverage

karma-coverageはテストのカバレッジをhtmlで出力することができます。


karma-coverageをインストール

npm install --save-dev karma-coverage


設定ファイルのreportersにcoverageを追加します。

coverage対象を定めるためにpreprocessorsに対象のjsファイルを追加します。


karma.conf.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を実行

karma start


プロジェクトフォルダ直下にcoverageディレクトリが作成されます。


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関数のテストを行っていないため赤くなりパーセンテージが下がってます。

Screen Shot 2016-05-12 at 10.35.32 PM.png


karma-junit-reporter

karma-junit-reporterはJUnitXML形式でテスト結果を出力できます。


karma-junit-reporterをインストール

npm install --save-dev karma-junit-reporter


設定ファイルのreportersにjunitを追加します。

junitReporter.outputDirに出力フォルダを指定します。(指定しない場合、プロジェクト直下にできて邪魔になります)


karma.conf.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', 'coverage'],
+ reporters: ['mocha', 'coverage', 'junit'],
+ junitReporter: {
+ outputDir: 'report'
+ },
logLevel: config.LOG_DEBUG
})
}


karmaを実行

karma start



reportディレクトリの中身

report

└── TESTS-Chrome_50.0.2661_(Mac_OS_X_10.11.2).xml

0 directories, 1 file


Screen Shot 2016-05-12 at 10.43.10 PM.png

ステップバイステップで始めるKarma Step6.png


その他 Karmaの実行時オプション

前項までに扱っていた内容ですとkarma startしか出てきませんでしたが

他にもkarma initkarma runという2つのコマンドがあります。

それぞれ解説します。


karma init

karma.conf.jsを対話形式で作るためのオプションです。

本記事では学習のために敢えて使いませんでした。

一度自分なりの設定ファイルを作ってしまったらそれをコピったほうが早いので気がします。

Screen Shot 2016-05-15 at 8.28.37 PM.png


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の中で出てきたcoffeePreprocessorkarma-coffee-preprocessorが独自に定義した項目です。

そのためKarmaのリファレンスには記載されていません。


basepath

filesで読み込むファイルのベースを指定できます。

デフォルトは''です。

karma.conf.jsをプロジェクト直下から変更した場合はいじったほうが記載が楽になります。


exclude

filesで登録したファイルから除外したいファイルを指定できます。

※files同様*を使った指定が可能です。


plugins

自分で書くことはないかもしれませんが、仕組み上重要なので記載しておきます。

npmで各種プラグインをインストールした後にkarma.conf.jsjasmineChromeと書くだけでなぜ使えるのかですが、このpluginsの設定のおかげです。

デフォルトでkarma-*が指定されているためkarma-*でマッチするnode_moduleを自動で読み込んでいるわけですね。

試しにplugins:[]と記載して実行すると以下のように見つからない旨のエラーになります。


pluginが見つからない場合のエラー

...

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を使えばテスト項目を絞ることができます。


karma.conf.js

config.set({

...
client: {
args: ['--grep', 'add関数のテスト']
}
})

karma runで指定することも可能です。以下のように--の後に指定します。


部分的にテストを実行

karma run -- --grep='add関数のテスト'



Tips オプションについて

例えばjasmineはspecFilterという機能を使ってテスト対象を絞り込むことが可能です。

grepを指定した場合、karma-jasmineがgrepの内容をjasmine上のspecFilterに登録してくれています。


その他 プラグインの探し方

npmjsで検索になるのでしょうか。

良いやり方がありましたら教えて下さい。


あとがき

Karma自体はそんなに内容は深くないので問題はないのですがプラグインに渡すプロパティの設定はちょっと厄介かもしれません。

プラグインのドキュメントが整備されていないとソースを見ないとちょっとわからないかもです。。