chai のプラグインを書いた & 書きかた
chai というアサーションライブラリがあって、テストフレームワークの Mocha と合わせてよく使われていると思うのだけど、その chai のプラグインを書いた。
このプラグインを使うと配列にも closeTo
が使えるようになる。
expect([ 1.5, [ 2.5, 3.5 ], 4.5 ]).to.be.deep.closeTo([ 1, [ 2, 3 ], 4 ], 0.5);
chai のプラグイン
chai はプラグインで便利機能を拡張する前提で設計されていて、各種プラグインが公開されている。たとえば有名なのではテストスパイとかスタブを扱う Sinon.JS を chai のインターフェースで使える sinon-chai とかがある。他にもある → Chai Plugins
chai のプラグインの書きかた
簡単に書ける。プラグインを適用するには以下のように chai.use
を使うのだけどそれに渡す関数を書くだけ。以下のコードは確認してないので、書き方だけ参考にしてください。
var chai = require("chai"); chai.use(function plugin(chai, _) { // ここにプラグインを書く });
引数の chai
というのは chai自体、_
は 便利機能群 です。
新しいメソッドを追加する
chai のアサーションはクラスメソッドを使って拡張できるようになっていて、例えば新しいメソッドを追加したい場合は Assertion.addMethod
を使う。
以下は絶対値が一致すれば OK というような absEqual
を追加する場合。
_.flag
というのは内部状態を参照したり変更したりする補助関数で、ここでは msg
があればメッセージをセットするのと、アサーション対象の値をゲットするのに使っている。
function plugin(chai, _) { var Assertion = chai.Assertion; Assertion.addMethod("absEqual", function(val, msg) { if (msg) { _.flag(this, "message", msg); // メッセージをセット } var obj = _.flag(this, "object"); // アサーション対象の値をゲット this.assert( Math.abs(val) === Math.abs(obj), "expected #{this} to equal #{exp}", "expected #{this} to not equal #{exp}", val // この値は ↑ の #{exp} に使われる // ここに値を書くと ↑ の #{act} に使われる ); }); }
完璧とは言いがたいけど、これでこういうアサーションが追加される。
expect(-10).to.absEqual(10); // OK
新しいプロパティを追加する
プロパティの追加は Assertion.addProperty
を使う。以下は NaN
のチェックをするやつ。
function plugin(chai, _) { var Assertion = chai.Assertion; Assertion.addProperty("NaN", function() { this.assert( isNaN(_.flag(this, "object")), "expected #{this} to be NaN", "expected #{this} to be not NaN" ); }); }
expect(-10).to.be.NaN; // "expected -10 to be NaN"
ちなみにアサーションは必ず必要というわけでなくて、フラグをセットするだけとか新しいアサーションを作るとかでも良い。 以下は配列をソートしてから以下に続けるやつ。
function plugin(chai, _) { var Assertion = chai.Assertion; Assertion.addProperty("sort", function() { var obj = _.flag(this, "object"); obj = obj.slice().sort(); return new Assertion(obj, _.flag(this, "message")); }); }
expect([ 5, 1, 3, 2, 4 ]).to.sort.and.eql([ 1, 2, 3, 4, 5 ]); // OK
既存のメソッドを上書きする
上書きはちょっと複雑で元のメソッド付きで関数が呼ばれるので、その中で上書き後の関数を返す。たとえば以下は closeTo
を上書きして actual と expected が一致する場合は equal
で評価して、そうでない場合はオリジナルの closeTo
を使うやつ。
function plugin(chai, _) { var Assertion = chai.Assertion; Assertion.overwriteMethod("closeTo", function(_super) { return function(expected, delta, msg) { var obj = utils.flag(this, "object"); if (obj == expected) { return this.equal(expected, msg); } return _super.apply(this, arguments); }; }); }
expect(Infinity).to.be.closeTo(Infinity, 0.5); // OK
だいたいこういう感じ。
あとは node.js とブラウザ両方で使えるようにするとか mocha
コマンドのときにプラグインを適用するとかあるのだけど、面倒なので興味があれば僕の書いたプラグインを参考にしてください。
気づき
バニラアイスにコーヒー豆を 2, 3粒挽いたのをまぶすと美味しい
参考
デフォルトのインターフェースも同じようにクラスメソッドを介して拡張されている
Plugin Utility API のドキュメント
上に書かれていないAPIもある