Edited at

jasmineの非同期テストをちょっとマシにした(気がする)

More than 5 years have passed since last update.


はじめに

 jasmineでユニットテストだうおー、と思ってたらどうも非同期系テストがダサい。何やねんこれ。フラグで管理て。以下は公式ドキュメントのサンプルコード

describe("Asynchronous specs", function() {

var value, flag;
it("should support async execution of test preparation and exepectations", function() {
runs(function() {
flag = false;
value = 0;
setTimeout(function() {
flag = true;
}, 500);
});
waitsFor(function() {
value++;
return flag;
}, "The Value should be incremented", 750);
runs(function() {
expect(value).toBeGreaterThan(0);
});
});
});


せめてFiberみたいにしたい

 Node.jsのFiberは非同期処理を同期っぽく書く事が出来るライブラリで、大体以下の様に書く。Fiber.yield()した所でブロッキングされ、fiber.run()で引数に値を渡したらyieldの戻り値として返ってきてブロッキングが解除される。

Fiber(function() {

var fiber = Fiber.current;
setTimeout(function() {
fiber.run("end");
}, 1000);
var result = Fiber.yield();
}).run();


FiberMini

 Fiberの様に非同期処理の呼び出し後にwaitを書いて、非同期処理が終わったらコールバック内などでnotifyする感じに出来るFiberMiniというのを作った。といっても凄く単純。残念ながらwaitでブロッキングは出来ないんで使い方に工夫が要る。あと凄く適当なのでjs的にOKな書き方なのか全く不明・・・。


FiberMini.js

var FiberMini = function () {

var self = {
isEnd: false,
dict: {},
run: function (f) {
f();
},
wait: function (timeout) {
if(typeof timeout === "undefined"){
timeout = 100000000;
}
waitsFor(function () {
if (self.isEnd) {
self.isEnd = false;
return true;
}
return self.isEnd;
}, "timeout", timeout);
},
notify: function () {
self.isEnd = true;
},
put: function (key, value) {
self.dict[key] = value;
},
get: function (key) {
return self.dict[key];
}
};
return self;
}

 使い方は以下の通り。jasmineのrunsはシーケンシャルに実行される性質を持ち、更にrunsの間にwaitForなどがあると次のrunsの実行をブロッキングしてくれる。なので非同期処理-待ち-続きの内、「続き」の部分は次のrunsに書けば良い。値の引き継ぎはfiber.putとfiber.getでやる事が出来る。まあクロージャでやってもええと思うが。呼び出しはconsole.logで書いている順番で実行される。

describe("async test", function () {

var fiber = new FiberMini();
fiber.run(function () {
console.log("1");
runs(function () {
console.log("4");
getXML("http://hoge.com/xml", function (xml) {
console.log("6");
fiber.put("xml", xml);
fiber.notify();
});
fiber.wait();
console.log("5");
});
console.log("2");
runs(function () {
console.log("7");
var xml = fiber.get("xml");
expect(xml).not.toEqual(null);
console.log("8");
});
console.log("3");
});
});


おわりに

 動作はjasmine-nodeとjasmineのstandaloneでやりました。それにしても何かもっと良い感じでお手軽なのないのかな・・・。