jQuery Monile 1.1.0 に対応した JQMDP-1.0rc2 を googlecode に置きました。
  • http://code.google.com/p/jqmdp/downloads/list

本題。
JavaScriptのテストコードってどうすれば良いかと調べると QUnit って言うのが見付かった。
  • 本家:http://docs.jquery.com/Qunit
  • 参考:http://d.hatena.ne.jp/Jxck/20100721/1279681676
使い方はほぼ JUnit と同じ。
特殊なのは JavaScript はスレッドが使えないので非同期処理をするための 仕掛けが用意されている事くらい。

参考サイトは非常に良くまとめてくれるいるので先に参照して欲しい。

で、現実的なテストの場合、アプリとテストコードは分離したいので 別Window(又は iframe)でテストアプリを起動してやる必要がある。

参考サイトにはこの辺の解説が無かったので自分で調べてみた。

本家サイトのデモを見ると基本系はこんな感じだが

module("Module name"); test("test1", function() { equal( true, true, "passing test" ); }); test("test2", function() { equal( true, false, "failing test" ); });

module()関数の第2引数にオプションを指定すると開始と終了をフックできる。

module("Module name", { setup: function(){ 開始処理 }, teardown: function{ 終了処理 } }); test("test1", function() { // ここで this は module()第2引数になる。 });

なのでおおざっぱに考えると

  • setup() で window.open("アプリURL");
    • onload でテスト起動
  • teardown() で window.close();
してやれば良さげ。

具体的にはこうなった。

var Sandbox = function(url){ this.url = url; this.win = null; this.onload = []; this.autoClose = true; } Sandbox.prototype = { setup: function() { var self = this; this.win = window.open(this.url, "_blank"); this._onload = function(){ var $handle = $(self.win.document); for (var i=0; i<self.onload.length; i++) { self.onload[i]($handle, self); } }; function checker() { if (self.win.document.readyState == "complete") { self._onload(); } else { setTimeout(checker, 50); } } setTimeout(checker, 100); }, teardown: function() { if (this.autoClose) this.win.close() }, load: function(callback){ this.onload.push(callback); } }; module("module name", new Sandbox("アプリURL")); test("test1", function() { stop(); this.load(function($sandbox){ // Sandbox.load() start(); same($sandbox.find("#id").val(), "正解", "項目詳細"); }); });

IEは別 Window の onload が取れないのでしかたなくポーリングにした。 IEいらないなら this.win.onload = function(){...} で良いはず。

Sandbox.load()で設定したコールバックが別Windowの onload で呼ばれるので、 引数の $sandbox (別Windowの$(document))から値を引っ張り出してチェックすれば良い。

PC の Chrome, IE, Safari で動作が確認できた。
が、残念ながら iPhone では動かなかったので iframe に変更してみた。

$(function(){ $(document.body).append($("<iframe id='sandbox'></iframe>")); }) var Sandbox = function(url){ this.url = url; this.win = null; this.onload = []; this.autoClose = true; } Sandbox.prototype = { setup: function() { var self = this; var ifr = document.getElementById("sandbox"); this.win = ifr.contentWindow; function onload(){ var $handle = $(self.win.document); for (var i=0; i<self.onload.length; i++) { self.onload[i]($handle, self); } }; ifr.onload = onload; // for IE ifr.onreadystatechange = function(){ if (this.readyState == "complete") { onload(); } }; ifr.src = this.url; }, teardown: function() { }, load: function(callback){ this.onload.push(callback); } };

iframe だとちゃんと iPhone でも QUnit が動いた。(iOS5、他未確認)

デバックは別Windowの方がやり易いので切替えられるようにしたいかな。

あと、普通のWebアプリのテストにも使えそうなので環境が面倒な Selenium の代わりになりそうに思います。