ここの所、数か月おきにjsのユニットテストってどうやるのが良いのか悩んでいる気がするので、一つ情報集約の為にメモ書きをしておきます。
何かちゃんと文章書いておけば、それに対する反応が集まって、オレサマハッピー的な展開を望んでいます。
単体テストというか、ユニットテストというか、そういうアレを書く為のフレームワークを探しています。
覚える事が少なくて強力なやつ。
機能テストというか、e2eテストいうか、そういうアレの事は別途考える必要がありますので、今回はスコープ外とします。
結局の所、今はJasmineが一番人気っぽいけどエッジな人々を観測していると、どうもmocha推しが増えている様な気がする。
そういうボンヤリした問題意識が僕の頭の中にうっすらとあってモヤモヤしているので良くない。
自分にとって納得いくようにキチンと整理してしまおうと言うのが、この文章の意図である訳です。
テスト対象のコードが、どの様なランタイムで動くのか?ってのは結構大事な事で、
それが違ったくらいで、テストコードの書き方が変わったりだとか作法に大幅なズレがあると辛いなぁ…と思う訳ですよ。
僕はテストがしたいんであって、テスティングフレームワークを覚えたいんでは無いのでね。
つまり、nodeとブラウザの両方で余り変な細工せずともちゃんと動いて欲しいのです。
xUnit気味なのか、BDD気味なのかってのは、単にキーワード的な違いでしかないのでは?と思っている。
describe/it スタイルとsuite/test スタイルは見た目上の違いしか無いよね。
僕としてはdescribeはタイピングし辛いので、余り好きではない。
サーバサイドをnodeに限定するつもりはなくて、僕の場合は恐らくnodeでプロトタイピングしたらjavaで作り直す位の事はする訳です。
隣に座ってる若いのはRubyistだし、今の一番弟子はPythonistaだし、変に凝り固まると老害呼ばわりされかねず、それは恐ろしい。
APIセットが非常に小さく覚えるべき事柄が少ないのが良いですね。
- nodejsのassertライブラリ
var actual = doSomething();
assert(2 > actual && actual < 13);
- chai.jsのassert API にあるoperatorメソッド
var actual = doSomething();
assert.operator(actual, '>', 2);
assert.operator(actual, '<', 13);
テストコードのアサート部分にコードを書いた人間の意図が十分に残らない可能性があるし、
言語機能を十分に理解していなければ適切なアサートコードを書く事は出来ません。
APIを覚えなくても良い代わりにイディオムの様なものを沢山覚えるか捻り出す必要があります。
可読性が上がるらしいです。僕にはその原理が良く分からないのですよね。
- Jasmineの場合、これしか選べません。
var actual = doSomething();
expect(actual).toBeGreaterThan(2);
expect(actual).toBeLessThan(13);
- should.js
var actual = doSomething();
actual.should.be.above(2);
actual.should.be.below(13);
- chai.js の expect API
var actual = doSomething();
expect(actual).to.be.above(2);
expect(actual).to.be.below(13);
expectから必ず始めるってのは、確かに分かり易い感はありますね。
しかしもってAPIが膨大にあって、そのボキャブラリをどれだけ抑えれば良いのか分からないのが嫌な感じがします。
言語機能を使うのではなく、妥当なAPIを使うべきとのスタンスはどうも応用性が低いのではないかなぁ…と思います。
所で、入力補完がそれ程上手く効く訳でもないのにJasmineのアサートメソッド名は長過ぎやしませんか?
shouldの黒魔術感は半端無いですね。 この記法はRSpec由来でしょうか…
簡単でそれなりに強力なものが入ってるのがJasmine。
sinon.jsを使うのがMocha。
- Jasmineのspy
var whatAmI = jasmine.createSpy('whatAmI');
whatAmI("I", "am", "a", "spy");
expect(whatAmI).toHaveBeenCalledWith("I", "am", "a", "spy");
- sinon.js
var whatAmI = sinon.spy();
whatAmI("I", "am", "a", "spy");
assert(whatAmI.calledWith("I", "am", "a", "spy"));
基礎的な部分だけ何となく眺めても違いが良く分からない程度にはJasmineのSpy APIは強力です。
加えて、sinon.jsとjasmineを接合するライブラリもあります。
javascriptでコードを書く以上、非同期処理とかコールバックとかpromiseとかコントロールフローとか、
何かそういうアレソレから逃れる事は出来ない訳で、そういうコードを上手くテストする事が望まれる訳です。
- Jasmineで非同期処理のテスト
- Mocha で非同期処理のテストは、Jasmineに比べると理解し易い。
describe('User', function() {
describe('#save()', function() {
it('should save without error', function(done) {
var user = new User('Luna');
user.save(done);
});
});
});
つまり、テストメソッドに引数として入ってくるdoneを呼べばそれでよろしいって話です。
Jasmineのruns/waitsForのペアは、趣味の問題かもしれないけど、僕にとっては凄く分り辛いですね。
僕としてはガッツリコントリビュートしているtestacularを推しておきますけども、testemも悪く無いと思いますよ、ええ。
ファイルの変更を検知してテストが実行される事に慣れると、
開発におけるテンポ感が根底から変化しますので、どれかを使うとよろしいかと思います。
Gruntjsのwatchタスクからガチャガチャやっても出来るかと思います。
testacular や testemを使うと、テスティングフレームワークのレポーターが拡張されていて、
ブラウザによるテストであるにも関わらずJUnitフォーマットのレポートが出せたりTAPフォーマットで標準出力出来たりする。
JUnitフォーマットのテストレポートがあるとJenkinsさんが良い感じに働いてくれるですよ。
testacularなら、Jasmine, Mocha, QUnitでコードカバレージ取れる。
mochaはコードカバレージ取る機能あるけど残りの2つには無いからねぇ。