espower-babel から babel-preset-power-assert への移行
私は Web フロントエンドのテストで mocha + power-assert + espower-babel を組み合わせて利用しているのだが、これらのうち espower-babel が Deprecated になった。経緯と代替については以下の記事にまとまっている。
これからは espower-babel 代わりに babel-register と babel-preset-power-assert を利用。babel-register で require
をフックして assert
を power-assert へ置き換える。この処理が Babel ビルドの一環として実行されるようになった、という理解でよいのだろうか。
移行には npm 更新、.babelrc
と mocha.opt
修正が必要。これを一発で実行するため migrate-espower-babel-to-babel-preset-power-assert というツールも用意されている。冒頭の記事にも使用方法が掲載されているけど自分でも試したいので内容を以下にまとめる。
migrate-espower-babel-to-babel-preset-power-assert
このツールは既に mocha + power-assert + espower-babel を利用してるプロジェクトを新構成へ移行するためのもの。複数プロジェクトで利用したいのでグローバルにインストール。
$ npm i -g migrate-espower-babel-to-babel-preset-power-assert
次に移行したいプロジェクトの package.json
の置かれた階層へ移動する。そして以下のようにコマンドを実行。
$ migrate-espower-babel-to-babel-preset-power-assert
Run: npm uninstall --save-dev espower-babel
Run: npm install --save-dev power-assert
Run: npm install --save-dev babel-preset-power-assert
Run: npm install --save-dev babel-register
rewrite mocha.opts
rewrite .babelrc
注意点がある。
test
ディレクトリ内に mocha.opts
が存在しないとエラーになる。空の mocha.opts
を配置した場合は出力に rewrite
と表示されもののファイルは空のまま。---compilers js:espower-babel/guess
を定義してから実行したら ---compilers js:babel-register
へ書き換えられた。
私は mocha.opts
を利用せず npm-scripts の test
で対応していたのでエラーになった。mocha についてこれを期に mocha.opts
を使うようにするか npm-scripts 完結のままにするか迷っている。
このコマンドを繰り返し実行すると .babelrc
の env.development.presets
に power-assert が複数追加される。移行ツールなのでコマンド実行は一度で十分だが mocha.opts
の有無による動作検証で何度も実行していたらこの問題に遭遇した。
手動による導入 or 移行
移行ツールでおこなっていることをそのまま手動で実行。
はじめに espower-babel をアンインストール。このコマンドは npm がなければ空振りするだけなので導入時に実行しても問題ない。
$ npm uninstall -D espower-babel
テストに必要な npm をインストール。テスト対象とテストの両方を ES2015 で実装、かつ React の JSX を含むので、そのためのプリセットも含めている。
$ nmp i -D mocha power-assert babel-preset-power-assert babel-register
$ npm i -D babel-preset-es2015 babel-preset-react
package.json
と同一階層に .babelrc
を作成、以下を記述して保存。
{
"presets": [
"es2015",
"react"
],
"env": {
"development": {
"presets": [
"power-assert"
]
}
}
}
mocha のオプションを test
ディレクトリ内の mocha.opts
で指定するならファイルを作成。以下を記述して保存。
---compilers js:babel-register
test/**/*.test.js
私のように mocha の実行とオプションを package.json
の npm-scripts で一括指定するなら以下のように記述。mocha.opts
ありなら test
コマンドの内容はオプションなしで mocha
だけとすること。
{
"scripts": {
"test": "mocha --compilers js:babel-register test/**/*.test.js"
}
}
mocha.opts
、npm-scripts ともに babel-register のフックを有効にして test
配下の *.test.js
というファイルをテスト対象にしている。わざわざ特別な拡張子にしている理由は以下。
- テキスト エディタのタブ上でテスト対象
*.js
とテスト*.test.js
を区別したい - モックやユーティリティなど、テストから参照されるがテスト コードを含まないファイル
*.js
をスキップしたい
既存プロジェクトで移行が正常であることを試すため test
コマンドを実行。
$ npm test
> electron-starter@1.0.7 test .../examples-electron/electron-starter
> mocha
Util
formatDate
✓ Default YYYY-MM-DD hh:mm:ss.SSS
✓ Hyphen YYYY-MM-DD-hh-mm-ss
✓ No zero-padding YYYY/M/D h:m:s
3 passing (17ms)
ばっちり。npm-scripts のみと mocha.opts
の両方を試したが、どちらも正常にテスト実行された。
ES2015 と assert
今回のテストに使用したコードは以下となる。
import assert from 'power-assert';
import Util from '../../src/js/common/Util.js';
/** @test {Util} */
describe( 'Util', () => {
/** @test {Util#formatDate} */
describe( 'formatDate', () => {
it( 'Default YYYY-MM-DD hh:mm:ss.SSS', () => {
const date = new Date( 2015, 7, 4, 21, 17, 45, 512 );
const text = Util.formatDate( date );
assert( text === '2015-08-04 21:17:45.512' );
} );
it( 'Hyphen YYYY-MM-DD-hh-mm-ss', () => {
const date = new Date( 2015, 7, 4, 21, 17, 45, 512 );
const text = Util.formatDate( date, 'YYYY-MM-DD-hh-mm-ss' );
assert( text === '2015-08-04-21-17-45' );
} );
it( 'No zero-padding YYYY/M/D h:m:s', () => {
const date = new Date( 2015, 7, 4, 21, 17, 45, 512 );
const text = Util.formatDate( date, 'YYYY/M/D h:m:s' );
assert( text === '2015/8/4 21:17:45' );
} );
} );
} );
見てのとおりテスト自体も ES2015 で記述されている。espower-babel と同じくテストと対象の両方で ES2015 を利用可能。プリセットを変えれば ES2016 や Polyfill などもゆける。
また、
import assert from 'power-assert';
は
import assert from 'assert';
でもよい。実際に import
を標準 assert
へ書き換えてから、わざとテストを失敗させてみる。
1 failing
1) Util formatDate Default YYYY-MM-DD hh:mm:ss.SSS:
AssertionError: # test/common/Util.test.js:11
assert(text === '2015-08-04 21:17:45.512')
| |
| false
"2016-08-04 21:17:45.512"
--- [string] '2015-08-04 21:17:45.512'
+++ [string] text
@@ -1,12 +1,12 @@
201
-5
+6
-08-04 2
+ expected - actual
-false
+true
at decoratedAssert (node_modules/empower/lib/decorate.js:42:30)
at powerAssert (node_modules/empower/index.js:65:32)
at Context.<anonymous> (Util.test.js:11:7)
エラー情報が詳細に表示されている。冒頭の記事で解説されていたように assert
そのものが power-assert 化されていることがわかる。
assert
を import
するようにしておけば将来 power-assert から標準 assert
へ戻したくなった時もコードを書き換えずに済む。例えば標準 assert
が power-assert 並に高機能化するとか、むしろ power-assert の実装を取り込むとかしたときには脱却することとなるかもしれない。
まとめ
espower-babel に比べて設定量は増えたが Babel を使うなら .babelrc は必要なので、そこに集約したほうが分かりやすくなる。
プロジェクトにあまりファイルを増やしたくないので mocha.opts
については採用すべきか迷うところ。しかし既に .babelrc
だけでなく esdoc.json
なんかも追加しているので、そういうものだと受け入れることにした。
Babel については package.json
の babel
プロパティでも代替できるらしい。けれど他のツールもこの方式をサポートしていなと管理がバラバラになりかえって混乱しそうだ。私の理想は
{
"tools": {
"babel": {},
"browserify": {}
"esdoc": {},
"mocha": {}
}
}
のような感じでツール専用のプロパティが提供され、その配下に Babel などのプロパティが属する設計。しかし既に実運用されているものを後付で統一されたルールに移行させるのは高い政治力が求められる。また package.json
の巨大化も受け入れられなさそうだから妄想にとどめておく。
babel-preset-power-assert 移行によりテスト対象の assert
も power-assert 化されるのはよい。デバッグ ビルドだけ assert
を有効にする (= リリース ビルドでは除去) なら邪魔にならないし、テスト時に assert
がヒットしたら詳細な情報にあたれるので assert
を積極的に書きたくなる。
最近の Web フロントエンド開発はビルドとプリプロセッサーを前提とすることで IDE のように高度な依存注入や変換を利用できて生産性が増している。ありがたいことだ。
最後に移行の実装例となるプロジェクトを挙げておく。