Webpackベースのアプリケーションをテストしたい
Webpackとは
Webpackはモジュールの依存関係を解決するプリプロセッサです。
依存関係の記述には、ES6、CommonJSやAMDのスタイルが使えます。
誤解を恐れずに言えば、node.js感覚で他のモジュールをrequireできるようにしてくれます。
webpackでソースコードをコンパイルすると、*.bundle.js
というような名前のファイルが出力されます。
これをアプリケーション側で事で、同期的・非同期的なモジュールの依存関係の解決が可能となります。
テストしやすい方法の検討
要件としては以下のとおりです:
- Webpackで書いたアプリケーションの各ユニットあるいは各ふるまいを、出来るだけ簡単にテストしたい
- 任意のCI(Continuous Integration)でも動かしたい
使用するツール群
前述の要件を満たすツールの組み合わせがたくさんあります。
今回は以下の構成でテストを走らせてみます:
- Karma – テストランナー
- PhantomJS – ヘッドレスブラウザ
- Mocha – テストフレームワーク
- Chai – アサーション
- webpack – プリプロセッサ
- bower – パッケージ管理
各ツールの詳しい説明は、それぞれの本家をご参照ください。
テストの実施準備手順
設定項目や手順の意味を理解するために、敢えてバラバラに分けて説明します。
筆者はMac OS Xの環境で行いました。
必要なパッケージのインストール
$ npm install --save-dev
karma
karma-chai
karma-mocha
karma-phantomjs-launcher
karma-webpack
Karmaの設定
karma.conf.js
で以下のファイルを作成します。
// Karma configuration
// Generated on Wed Nov 26 2014 23:12:23 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: ['mocha', 'chai'],
// list of files / patterns to load in the browser
files: [
'test/**/*_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: {
// add webpack as preprocessor
'test/**/*_test.js': ['webpack', 'sourcemap']
},
// 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: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// karma watches the test entry points
// (you don't need to specify the entry option)
// webpack watches dependencies
// webpack configuration
webpack: {
}
});
};
Bowerのモジュールを読み込めるようにする
設定ファイル冒頭に以下を追記します:
var webpack = require("webpack");
var path = require("path");
var bowerPath = path.join( __dirname, "bower_components" );
webpackの設定に以下を追記します(参考: usage with bower)
webpack: {
resolve: {
// Tell webpack to look in node_modules, then bower_components when resolving dependencies
// If your bower component has a package.json file, this is all you need.
modulesDirectories: ["node_modules", bowerPath]
},
// Define a new plugin that tells webpack to look at the main property in bower.json files when resolving dependencies.
// For marionette, we need it to load the CJS version, which we specify with as ["main", "1"] in the args below.
plugins: [
new webpack.ResolverPlugin([
new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin( "bower.json", ["main", ["main", "1"]] )
], ["normal", "loader"])
]
}
これで、テストのソースコードからbowerのモジュールをrequire("jquery");
というように記述して読み込めるようになりました。
テスト対象のサンプルモジュール
簡単なjqueryを用いるサンプルモジュールです。
index.js
で以下のようなファイルを用意してください。
var $ = require('jquery');
module.exports = function() {
var div = $('<div />');
$('body').append(div);
return div;
};
依存関係にある jquery
をbowerでインストールしておきます:
$ bower install --save jquery
テストの用意
test/main_test.js
で以下のようなファイルを作成します:
var createElement = require('../index.js');
describe('jQuery sample', function() {
it('should create div element', function() {
expect(createElement).to.be.Function;
var div = createElement();
expect(div).to.be.Array;
});
});
処理内容としては、先ほど作成したサンプル関数(createElement
)を読み込みます。
次にその関数を呼び出し、結果を検査しています。
テストの実行
以下のコマンドを実行します:
$ karma start
結果:
$ karma start
INFO [karma]: Karma v0.12.28 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
Hash: 606dfda4c9d20a78987a
Version: webpack 1.4.13
Time: 8ms
webpack: bundle is now VALID.
webpack: bundle is now INVALID.
Hash: 1b62ba3e4c6b38edee7e
Version: webpack 1.4.13
Time: 944ms
Asset Size Chunks Chunk Names
_js/test/main_test.js 693312 0 [emitted] test/main_test.js
chunk {0} _js/test/main_test.js (test/main_test.js) 247722 [rendered]
[0] ./test/main_test.js 245 {0} [built]
[1] ./index.js 126 {0} [built]
[2] ./bower_components/jquery/dist/jquery.js 247351 {0} [built]
webpack: bundle is now VALID.
INFO [PhantomJS 1.9.8 (Mac OS X)]: Connected on socket Mfny5tbQCtBeW9kEu0TR with id 80467111
PhantomJS 1.9.8 (Mac OS X): Executed 1 of 1 SUCCESS (0.011 secs / 0.002 secs)
動きましたね!
さらに改善する
ソースマップ
Webpackでコンパイルしたテストがコケた時、示される行番号が参考にできません。
ソースマップを有効にすることで、コンパイル前の行番号を取得できます。
$ npm install --save-dev karma-sourcemap-loader
preprocessorsに加えます:
preprocessors: {
'test/test_index.js': ['webpack', 'sourcemap']
}
また、webpackにソースマップを生成するよう設定します:
webpack: {
// ...
devtool: 'inline-source-map'
}
実行すると以下のように元の位置が併せて示されます:
PhantomJS 1.9.8 (Mac OS X) Hello test Is it right? FAILED
TypeError: '[object Object]' is not a function (evaluating 'should(T)')
at /Users/nora/Development/tmp/karma-test2/test/main_test.js:53:0 <- webpack:///./test/main_test.js:7:0
PhantomJS 1.9.8 (Mac OS X): Executed 1 of 1 (1 FAILED) ERROR (0.006 secs / 0 secs)