c4se記:さっちゃんですよ☆

.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)

.。oO(此のblogは、主に音樂考察Programming に分類されますよ。ヾ(〃l _ l)ノ゙♬♪♡)

音樂は SoundCloud に公開中です。

考察は現在は主に Scrapbox で公表中です。

Programming は GitHub で開發中です。

PhantomJSで簡単に非同期のtestを行ふ

JavaScriptは基本非同期で、callbackだったりevent drivenだったりする。最近はPromiseやgenerator (yield, async/await) も出てきてるが、此れも非同期だから、非同期処理を待つ事は考慮に入れなければならない。testを書く時も、当然個々のtest caseが、ほぼ確実に非同期に成る。

Gruntに於ける非同期処理

Gruntには非同期を待たせる仕組みがある。grunt.registerTask() のcallback内で使へるthisの、async() methodで、非同期処理の終了を通知する為の関数が得られる。全てが終わった時に此れを呼んでやれば好い。done() の引数はError若しくはnull/undefinedだ。
f:id:Kureduki_Maari:20140404184238p:plain

// 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で繋げてやる関数の順序が逆転してゐる事が解るから、然ふしてやる。
f:id:Kureduki_Maari:20140404184243p:plain
初期値として、最後に実行する関数を与える。各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は互いに独立の筈なので、害は無い。順序が必要な時は配列を使ふ事。