JavaScript Test with QUnit and mockjax

JavascriptのテストはXHRがネック

JavaScriptを書いていると、RESTサービスを利用してXHR(XMLHttpRequest)やjQuery.ajax経由で
JSONのやり取りをする処理を実装することが多い。

こういった処理は以下の理由からテストが書きにくい。
1.サーバサイドの実装状況に依存する
2.非同期処理

JavaScriptを始めたばかりの時はjsUnitを使ってテストを書いていたのだが、
この2点はいつもネックになっていた。

QUnitとmockjaxでだいたい解決する

新しいテストフレームワークをいろいろと試してみた結果、jQueryのテストフレームワークQUnit
jQueryライブラリのmockjaxを使えば自分が書く実装の大部分が解決できることがわかった。

QUnitについては色々と日本語の情報があるのだが、mockjaxについては
ほとんど見られなかったのでメモ書きとして残す。

jQuery.mockjaxってなに?

mockjaxはjQueryjQuery.ajaxにたいしてモックを提供してくれるjQueryプラグイン。
これを用いることでRESTサービスを利用するフロントエンドの開発を行う際に
サーバー側の実装が完了する前から通信部分の実装・テストを行うことができる。

mockjaxの使い方

簡単な使い方は以下のようになる。

こんな感じのJSONデータを受け取りたいとする。

{"status":"success",
"fortune":"Are you a turtle?"
}

うけとる処理はこんな感じ

function getFortune(){
  $.getJSON('/restful/fortune', function(response) {
      if ( response.status == 'success') {
          retun 'Your fortune is: '.concat(response.fortune);
      } else {
          return 'Things do not look good, no fortune was told';
      }
  });
}

通常、サーバー側のサービスが存在しないとこのコードはテストできない。
そこで、mockjaxを使ってテストコードの中でモックを作成する。

test("asynctest", function(){
    //モックを作成
    var id = $.mockjax({
        url: '/restful/fortune',
        responseTime: 750,
        responseText: {
            status: 'success',
            fortune: 'Are you a turtle?'
        }
    });

    //処理の実行
    var fortune = getFortune();
    stop();

    setTimeout(function(){
        start();
        //評価
        notEqual(fortune,'Things do not look good, no fortune was told', 'サーバーから結果を取得する');

        //mockのお片づけ
        $.mockjaxClear(id);
    }, 500);
});

これだけで、GETだろうがPOSTだろうがmockjaxが$.ajaxを拾って
あらかじめ設定しておいたレスポンスを返却してくれる。

「想定してなかった」ことに対するテストはやっぱり本物のサーバーサービスに接続する必要があるけど、
とりあえずの実装ならこのモックで十分事足りる。HTTP Statusも指定できるので、異常系のテストも書くことが出来る。

応用編1 urlの指定をする

mockjaxのurl指定はかなり柔軟に設定できる

  • 普通に指定
$.mockjax({
     url:'/url/to/service'
});
  • 特定のパス以下を全部拾う
$.mockjax({
     // /url/to/service も url/to/pathも該当する
     url:'/url/to/*'
});
$.mockjax({
     // url/to/path と url/to/service にはマッチするけど url/to/services にはマッチしない
     url: /^\/url\/to\/(path|service)$/i
});
応用編2 レスポンスの形式を変更する

最初の例でjsonで返却する例を示したが、テキスト・XMLでも返却できる

  • テキストで返す
$.mockjax({
     url:'/url/to/service',
     responseText:'response letter from server mockup'
});
$.mockjax({
     url: '/url/to/service',
     responseXML: '<document><quote>This is response letter!</quote></document>'
});
応用編3 proxyをかませる

特定のURLでリクエストをうけてリダイレクトできる

$.mockjax({
     //このURLで受けて
     url:'/url/to/service',
     //このURLにリダイレクトする
     proxy:'path/to/test.json'
});
応用編4 コールバック処理を指定する
$.mockjax({
     url:'/url/to/service',
     response: function() {
          //responseTextまたはresponseXMLを必ず設定する必要がある
          this.responseText = 'This is resposen letter!'
     }
});
応用編5 レスポンスパラメーターを指定する
$.mockjax({
     url: '/url/to/service',
     // レスポンスを一定時間遅らせる
     responseTime : 750,
     // HTTP Status Codeを指定する
     status : 200,
     // Content-Typeを指定する
     contentType : 'text/json',
     // HTTP response Headerを指定する
     header : {
          Access-Control-Allow-Origin : '*'
     },
     // リクエストにサーバータイムアウトを返却する
     isTimeout : true,
});
その他 モックオブジェクトの後片付け
  • すべてのモックオブジェクトを削除する
$.mockjaxClear();
  • 特定のモックオブジェクトを削除する
var mockId = $.mockjax({
     url : ....
});

$.mockjaxClear(mockId);

こんな感じ。長かった...。