ネットの海の片隅で

技術ネタの放流、あるいは不法投棄。

Jasmineでfixtureを読み込む

JavaScriptのテストでfixtureを使いたいと思うこと、ないですか?

JavaScriptユニットテストでfixtureを使いたい

JavaScriptのテストにおいて、特にXMLHttpRequestのレスポンスを利用する場合などは、

  • いちいちリクエストを投げるとテストが遅くなる
  • ネットワークがない環境ではテストできない
  • サーバが落ちてたらテストできなくなる
  • APIのコール数に限度がある

のような理由から、レスポンスを受け取ったつもりで引数に固定文字列を渡したり、リクエストを投げる部分にモックやスタブを使ったりすることが多いと思います。

想定するレスポンスが短ければテストコードに書いてしまえば良いのですが、レスポンスが数十行以上に及ぶことも少なくありません。 そして、長い文字列をテストコードにベタ書きするのは、

  • JavaScriptにはヒアドキュメントが備わっていない
  • そもそもテストコードとテスト用のデータは分離したい

などの観点から現実的ではありません。

つまり、JavaScriptの外部に保存したfixtureからレスポンスを読み込みたいわけです。

簡単にfixtureを読み込む機能はない

fixtureをファイルに保存するのは簡単にできると思います。 手書きでもコピペでもコマンドのリダイレクトでも好きなモノを使ってください。

しかし、JavaScriptにはファイル読み込みが備わっていないため、そのままでは保存したfixtureテストに使用することができません。

どうにかこうにかfixture的なことを実現する

ファイル読み込み関数がないため、XMLHttpRequestでローカルファイルを読み込むことにします。

ここでは、テストを行うためのjasmineはgemをつかってセットアップされているとします。 もし、jasmine-standaloneを使用している場合はpivotal/jasmine-gem · GitHubに従ってインストール・セットアップしてください*1

jasmine-gemが入っている状態でjasmine用のサーバを起動します。

$ rake jasmine

このjasmineサーバが起動している間、ローカルファイルへのHTTPリクエストが有効になります。 これを利用して以下のような関数を定義すると、XHRを通じてfixtureを読み込むことができます。

function fixture(path) {
  var req = new XMLHttpRequest();
  req.open("GET", path, false);
  req.send(null);
  return(req.response);
}

ちなみに

Chromeでjasmine-gemではなくjasmine-standaloneで同じことをしようとすると、

Cross origin requests are only supported for HTTP.

というエラーが吐かれます。

プロトコルにFileを用いた場合、ローカルのHTMLファイルからどこまで読み取れるか選手権 2011にある通り、ローカルにある外部ファイルに対する通信はクロスドメイン通信とみなされるようです。

この制約を回避してHTTPでのアクセスを行うためにjasmine-gemを使ってサーバを起動しています。

さいごに

JavaScriptを書き始めて日が浅い上、Jasmineを使うのも初めてなので、どうにかこうにか上記の方法でやりたいことを実現しました。

もっと良い方法があれば是非教えて欲しいです。

テスト駆動JavaScript テスタブルJavaScript

*1:理由は後述します。