JavaScriptは基本非同期で、callbackだったりevent drivenだったりする。最近はPromiseやgenerator (yield, async/await) も出てきてるが、此れも非同期だから、非同期処理を待つ事は考慮に入れなければならない。testを書く時も、当然個々のtest caseが、ほぼ確実に非同期に成る。
Gruntに於ける非同期処理
Gruntには非同期を待たせる仕組みがある。grunt.registerTask()
のcallback内で使へるthisの、async()
methodで、非同期処理の終了を通知する為の関数が得られる。全てが終わった時に此れを呼んでやれば好い。done() の引数はError若しくはnull/undefinedだ。
// Gruntfile.js module.exports = function(grunt) { grunt.initConfig({pkg : 'package.json'}); // 同期task grunt.registerTask('sync-task', function () { thisIsASyncCallback(); }); // 非同期task grunt.registerTask('async-task', function () { var done = this.async(); thisIsAnAsyncCallback(function (err) { done(err); }); }); };
this.async()
を呼んだ時点で、Gruntは其れが非同期のtaskだと判り、done() が呼ばれるのを待つ様に成る。だから this.async()
は同期的に呼んでやる。其の代はり done() を必ず呼んでやる事。
PhantomJSに於ける非同期テスト
PhantomJSとは、単にJavaScriptで自動動作させられるWebKit browserに過ぎないので、testを行ふ為の仕組みは存在してゐない。test用のmoduleを用意してrequire()するか、自分で書く事に成る。今回、library持ってきたりするのダルいから、自分で書く事を考へる。
其所で Array.prototype.reduce() を上手く使ふ。Promiseが有ればreduceで簡単に繋げるが (PromiseはMonadなので)、其れも無いので少し考へる。すると図の様に、reduceで繋げてやる関数の順序が逆転してゐる事が解るから、然ふしてやる。
初期値として、最後に実行する関数を与える。各reduce phaseは、自分より後に実行する関数の列 (callback連鎖) を受け取り、自分を実行した後に受け取った関数列を実行する、新たな関数列を返す。最後に、然ふやって得られた関数列を実行する。
(function () { /* global phantom */ 'use strict'; var tests = {}; tests['The site is moving.'] = function (done) { var page = require('webpage').create(); page.open('http://127.0.0.1/', function (status) { if (status !== 'success') { return done(new Error(status)); } done(); }); }; tests['Test 2'] = function (done) { thisIsAnAsyncCallback(done); }; tests['Test 3'] = function (done) { thisIsAnAsyncCallback(done); }; Object.keys(tests).reduceRight(function (deffer, testName) { var test; test = tests[testName]; return function (result) { if (result instanceof Error) { console.error(result); phantom.exit(); } if (result) { console.log(result); } console.log('Test: ' + testName); test(deffer); }; }, function (result) { if (result instanceof Error) { console.error(result); phantom.exit(); } if (result) { console.log(result); } phantom.exit(); })(); }());
tests
をhash tableにしてゐるのでほんたうは Object.keys()
の順序は保証されないが、今回は、test caseは互いに独立の筈なので、害は無い。順序が必要な時は配列を使ふ事。