script.aculo.usを読み解く

第9回 unittest.js(前編)

この記事を読むのに必要な時間:およそ 9 分

0184:  // Returns:
0185:  //  "ERROR" if there was an error,
0186:  //  "FAILURE" if there was a failure, or
0187:  //  "SUCCESS" if there was neither
0188:  getResult: function() {
0189:    var hasFailure = false;
0190:    for(var i=0;i<this.tests.length;i++) {
0191:      if (this.tests[i].errors > 0) {
0192:        return "ERROR";
0193:      }
0194:      if (this.tests[i].failures > 0) {
0195:        hasFailure = true;
0196:      }
0197:    }
0198:    if (hasFailure) {
0199:      return "FAILURE";
0200:    } else {
0201:      return "SUCCESS";
0202:    }
0203:  },

188~203行目のgetResultは,テスト全体の結果を返す関数です。実行時エラーが1つでもあれば"ERROR"が直ちに返ります。次に,失敗が1つでもあれば,"FAILURE"が返ります。何も無ければ"SUCCESS"が返ります。

0204:  postResults: function() {
0205:    if (this.options.resultsURL) {
0206:      new Ajax.Request(this.options.resultsURL, 
0207:        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
0208:    }
0209:  },

204~209行目のpostResultsは,テスト全体の結果('ERROR','FAILURE','SUCCESS')をoptions.resultsURLのアドレスにAjaxで送信する関数です。

0210:  runTests: function() {
0211:    var test = this.tests[this.currentTest];
0212:    if (!test) {
0213:      // finished!
0214:      this.postResults();
0215:      this.logger.summary(this.summary());
0216:      return;
0217:    }
0218:    if(!test.isWaiting) {
0219:      this.logger.start(test.name);
0220:    }
0221:    test.run();
0222:    if(test.isWaiting) {
0223:      this.logger.message("Waiting for " + test.timeToWait + "ms");
0224:      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
0225:    } else {
0226:      this.logger.finish(test.status(), test.summary());
0227:      this.currentTest++;
0228:      // tail recursive, hopefully the browser will skip the stackframe
0229:      this.runTests();
0230:    }
0231:  },

210~231行目のrunTestsは,テストを順番に実行する関数です。Test.Unit.Runnerの心臓部です。

211行目で,this.currentTestは,全体で何番めのテストを実行中かを表します。this.tests配列からテスト(Test.Unit.Testcaseのインスタンス)を取り出します。

212行目で,これ以上取り出せなければ,すべてのテストが終了です。以下の処理をします。

214行目で,終了時に,postResultsでテスト結果を送信します。

215行目で,終了時に,テスト全体の結果の要約を出力します。this.summaryは,後述するとおり,テスト全体の失敗の総数などの要約を返す関数です。

218行目で,テストにとって初めてのrunTestsの呼び出しかどうかは,このisWaitingフラグでわかります。初めてのときはログの出力先を作るために,表組みに1行追加します。

221行目で,テストを実行します。もしもこのrunの呼び出しの内部で,wait関数を呼ぶと,isWaitingフラグがtrueになります。

223,224行目が,wait関数の呼び出しがあった場合で,ログのメッセージ欄に'Waiting...'を表示します。

224行目で,タイマーで指定時間待ってから,再度runTestsを呼びます。そこで,wait関数の2番めの引数で渡した,続きの処理に移るというわけです。

226行目が,wait関数の呼び出しがなかった場合で,これ以降がテストの終了の処理です。ログにテストの実行結果を表示します。

227行目で,this.currentTestをインクリメントして,次のテストに進みます。

229行目で,runTestsを末尾再帰で呼び出します。コメントで,ブラウザのインタプリタがこれをループに最適化してくれることを願っています。

0232:  summary: function() {
0233:    var assertions = 0;
0234:    var failures = 0;
0235:    var errors = 0;
0236:    var messages = [];
0237:    for(var i=0;i<this.tests.length;i++) {
0238:      assertions +=   this.tests[i].assertions;
0239:      failures   +=   this.tests[i].failures;
0240:      errors     +=   this.tests[i].errors;
0241:    }
0242:    return (
0243:      (this.options.context ? this.options.context + ': ': '') + 
0244:      this.tests.length + " tests, " + 
0245:      assertions + " assertions, " + 
0246:      failures   + " failures, " +
0247:      errors     + " errors");
0248:  }
0249:}
0250:

232~250行目のsummaryは,テスト全体の結果の要約を出力する関数です。テスト全体の成功の総数,失敗の総数,エラーの総数を記述した文字列を返します。

著者プロフィール

源馬照明(げんまてるあき)

名古屋大学大学院多元数理科学研究科1年。学部生のときにSchemeの素晴らしさを知ったのをきっかけに,関数型言語の世界へ。JavaScriptに,ブラウザからすぐに試せる関数型言語としての魅力と将来性を感じている。

ブログ:Gemmaの日記