最近、ちょっと気になっていたテストランナー、Karmaを試してみます。
Karma - Spectacular Test Runner for Javascript
Karmaを使うと、実際のブラウザやPhantomJSでテストができたり、ファイルに変更があるとKarmaが検知してくれて自動実行してくれたりするみたいです。
PhantomJSと合わせられるところなどが良さそうだなぁと思い、試してみることにしました。
インストール用ドキュメントや
設定ドキュメントを参考にしながた使ってみたいと思います。
その他、このあたりも参考に。
イマドキのJSテスト - karma + karma-html2js-preprocessorでブラウザ/DOMを絡めたUIテストを実行する編 〜 JSおくのほそ道 #031
小さくKarmaとmochaでテスト環境を整える。 | niwaringo() {Tumblr}
karma+mocha+power-assertでDOM操作を含むユニットテストをES6で書く | WebDesign Dackel
環境としては、
を組み合わせて使うことにしたいと思います。
Karmaだけでテストが実行できるわけではないので、実際のテストコードとしてはMocha、Chaiなどが別途必用になります。今回は、この2つを利用しました。
準備
まずは、必要なnpmモジュールをインストールします。
## Babel $ npm install --save-dev babelify babel-preset-es2015 $ npm install --save babel-polyfill ## Browserify $ npm install --save-dev browserify watchify uglify-js ## Mocha, Chai $ npm install --save-dev mocha chai ## Karma $ npm install --save-dev karma karma-browserify karma-chai karma-mocha ## Karma+Chrome $ npm install --save-dev karma-chrome-launcher
「karma-〜」と付いているのは、だいたいは他のツールなどとの連携用です。また、Chromeと組み合わせるので「karma-chrome-launcher」もインストールします。
KarmaとBrowserifyを組み合わせてかつ変更を監視する場合は、Watchifyが必須みたいです。UglifyJs2は、テーマとは直接関係ありませんが、Minify用…。
ES2015を使うために、.babelrcも作成。
$ echo '{ "presets": ["es2015"] }' > .babelrc
Karmaの設定ファイルを作る
Karmaを使うには、設定ファイルを作成する必要があるようです。「karma init」で作成します。
$ ./node_modules/karma/bin/karma init
対話形式で進んでいくと、karma.conf.jsというファイルができます。
こちらをさらに編集し、今回はこういう内容にしました。
karma.conf.js
// Karma configuration // Generated on Sat May 07 2016 17:27:20 GMT+0900 (JST) module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['browserify', 'mocha', 'chai'], browserify: { debug: true, transform: ['babelify'] }, // list of files / patterns to load in the browser files: [ 'test/**/*.js' ], // list of files to exclude exclude: [ ], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { "src/**/*.js": ["browserify"], "test/**/*.js": ["browserify"] }, // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ['progress'], // web server port port: 9876, // enable / disable colors in the output (reporters and logs) colors: true, // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes autoWatch: true, // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher browsers: ['Chrome'], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun: false, // Concurrency level // how many browser should be started simultaneous concurrency: Infinity }) }
利用するフレームワークの設定として、browserify、mocha、chaiを追加。
// frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['browserify', 'mocha', 'chai'],
Browserifyの設定としては、Babelを利用するようにします。
browserify: { debug: true, transform: ['babelify'] },
ソースとテストコードにかかるPre-Processorにも、Browserifyを設定。
// preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { "src/**/*.js": ["browserify"], "test/**/*.js": ["browserify"] },
テスト対象のコードとテストコードを作る
それでは、テスト対象のコードとテストコードを作っていきます。
まずは、ディレクトリ作成(distはオマケです)。
$ mkdir src test dist
ブラウザ上で動くということなので、documentなどを使った適当なコードを書いてみます。
src/hello-karma.js
export function getElementClasses(element) { let classes = element.className.split(" "); return classes; }; export function getText(element) { return element.textContent; }; export function createTextNode(text) { return document.createTextNode(text); };
テストコード。
test/hello-karma-test.js
const should = require("chai").should(); describe("Karma Getting Started", () => { it("getElementClasses:success", () => { const helloKarma = require("../src/hello-karma"); let span = document.createElement("span"); span.setAttribute("class", "foo bar"); span.appendChild(document.createTextNode("Hello World")); helloKarma.getElementClasses(span).should.deep.equal(["foo", "bar"]); }); it("getText:success", () => { const helloKarma = require("../src/hello-karma"); let span = document.createElement("span"); span.setAttribute("class", "foo bar"); span.appendChild(document.createTextNode("Hello World")); helloKarma.getText(span).should.equal("Hello World"); }); it("createText:success", () => { const helloKarma = require("../src/hello-karma"); let span = document.createElement("span"); span.setAttribute("class", "foo bar"); span.appendChild(helloKarma.createTextNode("Hello World")); helloKarma.getText(span).should.equal("Hello World"); }); it("getElementClasses:fail", () => { const helloKarma = require("../src/hello-karma"); let span = document.createElement("span"); span.setAttribute("class", "foo bar"); span.appendChild(document.createTextNode("Hello World")); helloKarma.getElementClasses(span).should.deep.equal(["foo", "fuga"]); }); it("getText:fail", () => { const helloKarma = require("../src/hello-karma"); let span = document.createElement("span"); span.setAttribute("class", "foo bar"); span.appendChild(document.createTextNode("Hello World")); helloKarma.getText(span).should.equal("Hello Karma"); }); it("createText:fail", () => { const helloKarma = require("../src/hello-karma"); let span = document.createElement("span"); span.setAttribute("class", "foo bar"); span.appendChild(helloKarma.createTextNode("Hello World")); helloKarma.getText(span).should.equal("Hello Karma"); }); });
成功するケースと失敗するケースを、それぞれ3つずつ用意してみました。
実行
package.jsonに、以下の用に記述してnpm runから起動してみます。
"scripts": { "test:karma": "karma start karma.conf.js", "test": "echo \"Error: no test specified\" && exit 1" },
「test:karma」で起動するようにしました。
実行。
$ npm run test:karma
すると、Chromeが起動してテストが実行されます。
コンソールには、このように。
07 05 2016 23:36:14.892:WARN [karma]: No captured browser, open http://localhost:9876/ 07 05 2016 23:36:14.911:INFO [karma]: Karma v0.13.22 server started at http://localhost:9876/ 07 05 2016 23:36:14.929:INFO [launcher]: Starting browser Chrome 07 05 2016 23:36:18.653:INFO [Chrome 50.0.2661 (Linux 0.0.0)]: Connected on socket /#7gqSuSXi-99mmrMaAAAA with id 72312583 Chrome 50.0.2661 (Linux 0.0.0) Karma Getting Started getElementClasses:fail FAILED AssertionError: expected [ 'foo', 'bar' ] to deeply equal [ 'foo', 'fuga' ] at Assertion.assertEqual (/tmp/17ea1dc18d2f3459fee06c8c96b71486.browserify:2715:19 <- node_modules/chai/lib/chai/core/assertions.js:485:0) at Assertion.ctx.(anonymous function) [as equal] (/tmp/17ea1dc18d2f3459fee06c8c96b71486.browserify:6133:25 <- node_modules/chai/lib/chai/utils/addMethod.js:41:0) at Context.<anonymous> (/tmp/17ea1dc18d2f3459fee06c8c96b71486.browserify:8126:56 <- test/hello-karma-test.js:41:55) Chrome 50.0.2661 (Linux 0.0.0) Karma Getting Started getText:fail FAILED AssertionError: expected 'Hello World' to equal 'Hello Karma' at Context.<anonymous> (/tmp/17ea1dc18d2f3459fee06c8c96b71486.browserify:8136:41 <- test/hello-karma-test.js:51:40) Chrome 50.0.2661 (Linux 0.0.0) Karma Getting Started createText:fail FAILED AssertionError: expected 'Hello World' to equal 'Hello Karma' at Context.<anonymous> (/tmp/17ea1dc18d2f3459fee06c8c96b71486.browserify:8146:41 <- test/hello-karma-test.js:61:40) Chrome 50.0.2661 (Linux 0.0.0): Executed 6 of 6 (3 FAILED) (0.228 secs / 0.027 secs)
半分失敗するテストを書いているので、3つ失敗した、と言われていますね。
この後、ソースコードやテストコードを修正したりすると、Karmaが変更を検知して再度テストを実行してくれます。
とりあえず、基礎的な使い方はわかった感じでしょうか。
オマケ
UglifyJs2を依存関係に加えていましたが、一緒にJavaScriptをビルドする設定も書いておきます。
今回は、単純にnpm runで行うようにしました。
package.jsonに、以下のように記述。
※main.jsは、別途用意したエントリポイントです
"scripts": { "build": "browserify src/main.js -t [babelify] -o dist/app.js && cd dist && uglifyjs --compress --source-map app.js.map --source-map-url ./app.js.map --output app.js app.js", ... },
「npm run build」で、Browserifyでまとめ、UglifyJs2でminifyしつつ、Sourcemapを作成します。
できあがったコードは、「dist/app.js」として、Sourcemapは「dist/app.js.map」として出力されます。