Riot.js Advent Calendar 2016の18日目です!(遅刻組です…)
Riot.js(以下riot)は非常にシンプルかつ軽量で敷居も低く、とても書きやすいコンポーネント指向のUIライブラリです。(ここまでテンプレート) 今回はriotのテストに関する記事が少ないなーと思い、自分のテスト環境についてお話したいと思います。
はじめに
今回テストするデモアプリについて以下になります。riot-route
を用いた簡単なSPAアプリですね。デザインはMaterial Design Liteを使用してます。
※Riotのテストと宿命の反乱(karma-riot)と大筋はあまり変わりません。違うところは、webpack
を使っているくらいです。
ではテスト環境を揃えましょう
▼必要なモジュールのインストール
$ npm i -D karma karma-riot karma-mocha karma-phantomjs-launcher mocha riot
メインのライブラリはmocha
とkarma
です。他にもJasmine
やChai
やexpect.js
などもありますのでお好みのものをインストールして下さい。また、launchar
にはphantomjs
を使っています。ここはchrome
だったりfirefox
だったり、お好みのものを…(ry karma-mocha-reporter
も自分の好みですのでなくても問題ないです
ついでにpackage.jsonのscripts
にtest
を指定しておきましょう!
"scripts": {
"build": "webpack -d --watch",
"start": "webpack-dev-server --inline --hot --progress --colors --content-base ./"
+ "test": "karma start test/karma.conf.js"
}
}
これをしないと、karma
をグローバルインストールしないといけないので、書く方が面倒くさい!って方はこの一行は追加せず、コマンドラインから
$ npm i -g karma
を叩いて下さい。
▼ディレクトリ構成
ディレクトリ構成は以下のようになっております。
./
├ node_modules
├ package.json
├ webpack.config.js
├ index.html
├ tag
│ └ app.tag
│
├ build
│ └ bundle.js // デモアプリがwebpackを使っているためこの子が登場
│
└ test
├ karma.conf.js
└ spec/app.js
▼webpack
の設定の変更
{
entry: {
app: [
'./src/foo.js',
'./src/bar.js'
- ]
+ ],
+ test: [
+ './test/spec/app.js'
+ ]
},
output: {
path: __dirname + '/build/',
- filename: 'bundle.js'
+ filename: '[name].bundle.js'
},
…
}
上記のように、test用とbuild用のバンドルファイルを個別に出力するように設定し直します。この後のkarma.conf.js
でtest.bundle.js
を読み込みます。
▼karma
の設定
お次はkarma
の設定です。自分は大体以下のように書いてます。
let TEST_PATH = '/path_to_js_dir/test.bundle.js'
let TAG_PATH = '/path_to_tag_dir/*.tag'
module.exports = function(config) {
config.set({
basePath: '', // ここに指定してもいいけど自分はあまり使わないです
frameworks: ['mocha', 'riot'],
plugins: [
'karma-mocha',
'karma-mocha-reporter',
'karma-phantomjs-launcher',
'karma-riot'
],
// 以下がファイルを指定する部分
files: [
TEST_PATH,
TAG_PATH
],
preprocessors: {
'../**/*.tag': ['riot']
},
port: 9876, // 他のツールとportがバッティングしないように注意
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'], // お好みでlauncherを指定
reporters: ['mocha'], // デフォルトだと `progress`(指定しなくても良かったかも)
singleRun: true,
concurrency: Infinity
})
}
細かい設定は好みのものに適宜変更して下さい。
余談ですが、spec
ってspecified
の省略なんですね〜。
テストコード
今回のデモのテストコードはこちら!
var assert = require('assert')
var route = require('riot-route')
require('../../tag/app.tag') // これが何故必要なのか不明…
describe("Routing Demo Test", function () {
var tag = {}
before(function () {
// create mounting points
var html = document.createElement('app');
document.body.appendChild(html);
});
it('app mount check', function () {
tag = riot.mount('app', {
header: 'routing demo by riot v3',
navs : [
{ id: 'First', name: 'foo'},
{ id: 'Second', name: 'bar'},
{ id: 'Third', name: 'piyo'}
]
})
assert(tag[0].isMounted === true)
assert(document.querySelector('h2').innerText === 'No Select');
});
it('routing check', function () {
// #First
route('First')
assert(document.querySelector('h2').innerText === 'foo');
// #Second
route('Second')
assert(document.querySelector('h2').innerText === 'bar');
// #Third
route('Third')
assert(document.querySelector('h2').innerText === 'piyo');
});
it('app unmount check', function() {
tag[0].unmount(true)
assert(tag[0].isMounted === false)
});
})
実行!
上記の設定で実行すると以下のように出力されていれば成功です!
色々コケたところ
▼何故かtagファイルが読み込まれていない
undefined is not an object (evaluating '__TAG_IMPL[tagName].class')
先程のテストコードの3行目の部分です。ぶっちゃけ、このエラーに一番ハマりましたが原因不明です… gulp
を使って、色々タスクを走らせながら開発しているときはこのエラーにはぶつからなかったんですが、webpack
は奥が深いですね。。。今回はテストコードの冒頭にて必要なタグをrequireすることで回避しました。
▼v3からriot.route
がriot-route
(別モジュール)になったこと
バージョンアップの影響で、riot-route
を外部モジュールとしてrequireする必要があります。jsファイルやtagファイルの冒頭にconst route = require('riot-route')
と書いてあげましょう。
※karma-riot-route
があるのかな〜と思ってnpmを探しましたが見つかりませんでした。誰か作ってくれないかな〜(ワクワク)
▼require
が使えない
ReferenceError: Can't find variable: require
本来このエラーには出会うことはないはずです。というのも、上記でテストを書いたspecファイルもwebpackでバンドルしているからです。このエラーに出会うためには、__テストコードにはwebpackを使っていない__場合になります。
対処法は(実は過去に地味にハマりました)karma-webpack
を入れて、karma.conf.js
にて
preprocessors: {
・・・
+ 'spec/*.js': ['webpack']
}
と指定してあげましょう。
▼es6で書くとエラーになる
function() {}
の省略記法() => {}
を使うと、
SyntaxError: Unexpected token '>'
というエラーに会いました。
webpack.config.js
の設定だと思うのですが、色々やってみて上手くいかなかったので、今回はes6は断念しました…すげぇ悔しいのでどなかたwebpackに詳しい方お知恵をお貸しいただけますと、筆者泣いて喜びます!
終わりに
__そもそもriotのテストはwebpackを使わなくてもいいのでは…__とか思いました(笑)この記事を書こう!と決めたときに何故気づかなかったのか…(疲れていたということにしよう←) webpack
やrollup
のバンドルツールは、結局生成されるファイルは読み込んだモジュールを纏めてしまったものですよね(でも今回は結果的にこのスタイルじゃないと動きませんでしたが…?)。
本来単体テストは、specファイルそれぞれのテストケースは、そのファイル内(モジュール内)で完結した方が良いと個人的には思っています。その点riotは、tagファイルが一つのモジュールなのでそこが利点でもあるんです。たまにobservable
使ってモジュール間でやり取りする場合がありますが、その場合は、それらのモジュール群を一つのモジュールと見れば良いのです。
ですので、テスト用のspecファイルまでwebpackでバンドルする必要はないなーと。もちろんバンドルする利点もありますが、ここまで来ると好みの問題です。(karma.conf.js
のfiles
で必要なtagファイルを丸っと指定して、それぞれのspecファイルでrequireした方が分かりやすいし扱いやすいんじゃないかなと)
総合的に「webpack + karma + riotでのテストはそんなに簡単ではない」と感じましたw(先人の先輩方のテストの書き方を真似しながら拡張するほうが早かった)
実際の現場では、テストはバンドルせず流して、それが問題なければ、ビルドにはwebpackやらrollupやらを使ってバンドルすればいいかなと。以上!